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.
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