maglev-webtools 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. data/LICENSE.txt +25 -0
  2. data/README.rdoc +121 -0
  3. data/bin/webtools +15 -0
  4. data/lib/web_tools/#debugger.rb# +212 -0
  5. data/lib/web_tools/browser.rb +45 -0
  6. data/lib/web_tools/debugger.rb +219 -0
  7. data/lib/web_tools/info.rb +29 -0
  8. data/lib/web_tools/middleware/debugger.rb +118 -0
  9. data/lib/web_tools/support/app_model.rb +117 -0
  10. data/lib/web_tools/support/code_browser.rb +109 -0
  11. data/lib/web_tools/support/ruby.rb +132 -0
  12. data/lib/web_tools/support/service_helper.rb +22 -0
  13. data/lib/web_tools/support/smalltalk_extensions.rb +65 -0
  14. data/lib/web_tools/support/smalltalk_tools.rb +16 -0
  15. data/lib/web_tools/ui.rb +67 -0
  16. data/lib/web_tools.rb +10 -0
  17. data/public/images/favicon.ico +0 -0
  18. data/public/javascript/CodeMirror/LICENSE +23 -0
  19. data/public/javascript/CodeMirror/css/Smalltalk.css +34 -0
  20. data/public/javascript/CodeMirror/js/codemirror.js +582 -0
  21. data/public/javascript/CodeMirror/js/editor.js +1671 -0
  22. data/public/javascript/CodeMirror/js/highlight.js +68 -0
  23. data/public/javascript/CodeMirror/js/parseSmalltalk.js +126 -0
  24. data/public/javascript/CodeMirror/js/parsedummy.js +32 -0
  25. data/public/javascript/CodeMirror/js/select.js +699 -0
  26. data/public/javascript/CodeMirror/js/stringstream.js +159 -0
  27. data/public/javascript/CodeMirror/js/tokenize.js +57 -0
  28. data/public/javascript/CodeMirror/js/undo.js +413 -0
  29. data/public/javascript/CodeMirror/js/util.js +133 -0
  30. data/public/javascript/CodeMirror/testSmalltalkParser.html +116 -0
  31. data/public/javascript/ace/ace-uncompressed.js +17299 -0
  32. data/public/javascript/ace/ace.js +1 -0
  33. data/public/javascript/ace/keybinding-emacs.js +1 -0
  34. data/public/javascript/ace/keybinding-vim.js +1 -0
  35. data/public/javascript/ace/mode-c_cpp.js +1 -0
  36. data/public/javascript/ace/mode-clojure.js +1 -0
  37. data/public/javascript/ace/mode-coffee.js +1 -0
  38. data/public/javascript/ace/mode-csharp.js +1 -0
  39. data/public/javascript/ace/mode-css.js +1 -0
  40. data/public/javascript/ace/mode-groovy.js +1 -0
  41. data/public/javascript/ace/mode-html.js +1 -0
  42. data/public/javascript/ace/mode-java.js +1 -0
  43. data/public/javascript/ace/mode-javascript.js +1 -0
  44. data/public/javascript/ace/mode-json.js +1 -0
  45. data/public/javascript/ace/mode-lua.js +1 -0
  46. data/public/javascript/ace/mode-markdown.js +1 -0
  47. data/public/javascript/ace/mode-ocaml.js +1 -0
  48. data/public/javascript/ace/mode-perl.js +1 -0
  49. data/public/javascript/ace/mode-php.js +1 -0
  50. data/public/javascript/ace/mode-python.js +1 -0
  51. data/public/javascript/ace/mode-ruby.js +1 -0
  52. data/public/javascript/ace/mode-scad.js +1 -0
  53. data/public/javascript/ace/mode-scala.js +1 -0
  54. data/public/javascript/ace/mode-scss.js +1 -0
  55. data/public/javascript/ace/mode-svg.js +1 -0
  56. data/public/javascript/ace/mode-textile.js +1 -0
  57. data/public/javascript/ace/mode-xml.js +1 -0
  58. data/public/javascript/ace/theme-clouds.js +1 -0
  59. data/public/javascript/ace/theme-clouds_midnight.js +1 -0
  60. data/public/javascript/ace/theme-cobalt.js +1 -0
  61. data/public/javascript/ace/theme-crimson_editor.js +1 -0
  62. data/public/javascript/ace/theme-dawn.js +1 -0
  63. data/public/javascript/ace/theme-eclipse.js +1 -0
  64. data/public/javascript/ace/theme-idle_fingers.js +1 -0
  65. data/public/javascript/ace/theme-kr_theme.js +1 -0
  66. data/public/javascript/ace/theme-merbivore.js +1 -0
  67. data/public/javascript/ace/theme-merbivore_soft.js +1 -0
  68. data/public/javascript/ace/theme-mono_industrial.js +1 -0
  69. data/public/javascript/ace/theme-monokai.js +1 -0
  70. data/public/javascript/ace/theme-pastel_on_dark.js +1 -0
  71. data/public/javascript/ace/theme-solarized_dark.js +1 -0
  72. data/public/javascript/ace/theme-solarized_light.js +1 -0
  73. data/public/javascript/ace/theme-textmate.js +1 -0
  74. data/public/javascript/ace/theme-twilight.js +1 -0
  75. data/public/javascript/ace/theme-vibrant_ink.js +1 -0
  76. data/public/javascript/ace/worker-coffee.js +1 -0
  77. data/public/javascript/ace/worker-css.js +1 -0
  78. data/public/javascript/ace/worker-javascript.js +1 -0
  79. data/public/javascript/webtools/#debugger.coffee# +253 -0
  80. data/public/javascript/webtools/browser.js +260 -0
  81. data/public/javascript/webtools/debugger.coffee +286 -0
  82. data/public/javascript/webtools/debugger.js +366 -0
  83. data/public/javascript/webtools/sessions.coffee +17 -0
  84. data/public/javascript/webtools/sessions.js +27 -0
  85. data/public/javascript/webtools/version.coffee +14 -0
  86. data/public/javascript/webtools/version.js +20 -0
  87. data/public/stylesheets/base/images/ui-anim_basic_16x16.gif +0 -0
  88. data/public/stylesheets/base/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  89. data/public/stylesheets/base/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  90. data/public/stylesheets/base/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  91. data/public/stylesheets/base/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  92. data/public/stylesheets/base/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  93. data/public/stylesheets/base/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  94. data/public/stylesheets/base/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  95. data/public/stylesheets/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  96. data/public/stylesheets/base/images/ui-icons_222222_256x240.png +0 -0
  97. data/public/stylesheets/base/images/ui-icons_2e83ff_256x240.png +0 -0
  98. data/public/stylesheets/base/images/ui-icons_454545_256x240.png +0 -0
  99. data/public/stylesheets/base/images/ui-icons_888888_256x240.png +0 -0
  100. data/public/stylesheets/base/images/ui-icons_cd0a0a_256x240.png +0 -0
  101. data/public/stylesheets/base/jquery.ui.accordion.css +12 -0
  102. data/public/stylesheets/base/jquery.ui.all.css +2 -0
  103. data/public/stylesheets/base/jquery.ui.autocomplete.css +39 -0
  104. data/public/stylesheets/base/jquery.ui.base.css +11 -0
  105. data/public/stylesheets/base/jquery.ui.button.css +35 -0
  106. data/public/stylesheets/base/jquery.ui.core.css +37 -0
  107. data/public/stylesheets/base/jquery.ui.datepicker.css +61 -0
  108. data/public/stylesheets/base/jquery.ui.dialog.css +13 -0
  109. data/public/stylesheets/base/jquery.ui.progressbar.css +4 -0
  110. data/public/stylesheets/base/jquery.ui.resizable.css +13 -0
  111. data/public/stylesheets/base/jquery.ui.selectable.css +3 -0
  112. data/public/stylesheets/base/jquery.ui.slider.css +17 -0
  113. data/public/stylesheets/base/jquery.ui.tabs.css +11 -0
  114. data/public/stylesheets/base/jquery.ui.theme.css +247 -0
  115. data/public/stylesheets/jquery.contextMenu.css +62 -0
  116. data/public/stylesheets/reset.css +18 -0
  117. data/public/stylesheets/webtools.css +53 -0
  118. data/public/test.html +47 -0
  119. data/views/browser.rhtml +63 -0
  120. data/views/debugger.rhtml +59 -0
  121. data/views/index.rhtml +8 -0
  122. data/views/layout.rhtml +24 -0
  123. data/views/sessions.rhtml +12 -0
  124. data/views/version.rhtml +10 -0
  125. 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