rack_console 0.1.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 799230f90ccbe0900ea4e440b15c87f4af253e2f
4
- data.tar.gz: c0260e60d80014ab382281852625038776f72141
3
+ metadata.gz: 52ea8e7199b864ee9eb5771258dcdba1b64181e5
4
+ data.tar.gz: 42b0d1681e6d1fd6e679d8ed97f161f4e4b057f9
5
5
  SHA512:
6
- metadata.gz: 8d51f0582e263cc6f1eca189845e582517443b85d6174280d16ad8a8ef6bc5db7a171793b384cb0c73da4b8bab3c92c8762b22c3b723932b229f015b192fa067
7
- data.tar.gz: abf23fbb9b8f2a3d755262da1716ff1dc8b396527029d41cf5b3bf851a6b50ebc9c1c39d4ed4586729fa134c6678aa4c360195b07311e1edce5fbfac879b9351
6
+ metadata.gz: 887920281e5e9507852fc055805ee8185ae52f3fee4a2ad918312ea39cee800236e6f4f04a3ea3f5763c6b911d44a9921093b8a984994d60cf0d2531c3406a38
7
+ data.tar.gz: f5160c4bbfdba16cecea4bd74ff0d097cc8e22f653f29553fc20c503c0d8ac66862f5f51d398ef9df2b782de5334aa8a3a680c2cbd8f0425c06bbc7316dde659
@@ -0,0 +1,13 @@
1
+ require 'sinatra'
2
+ require 'tilt/haml'
3
+ require 'haml'
4
+ gem 'awesome_print'; require 'ap'
5
+
6
+ class App < Sinatra::Application
7
+ set :views, "#{File.expand_path('..', __FILE__)}/template/haml"
8
+
9
+ get '/?' do
10
+ haml :'index'
11
+ end
12
+ end
13
+
@@ -0,0 +1,15 @@
1
+ $:.unshift "../../lib"
2
+ $:.unshift "."
3
+ require 'rack_console/app'
4
+ require 'pry'
5
+ require 'app'
6
+ use Rack::Reloader
7
+ use Rack::Static, :urls => ["/css", "/img"], :root => "public"
8
+ run Rack::URLMap.new(
9
+ "/console" => RackConsole::App.new(
10
+ awesome_print: true,
11
+ url_root_prefix: "/console",
12
+ views: [ 'template/haml', :default ]),
13
+ "/" => App.new
14
+ )
15
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,19 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title rack_console
5
+ %link(rel="stylesheet" href="/console/css/rack_console.css")
6
+ %link(rel="stylesheet" href="/console/css/ansi.css")
7
+ %body
8
+ .wrapper
9
+ .header
10
+ %h1
11
+ %a{href: "/console"}rack_console
12
+ %h2 An interactive Ruby console as a Rack App.
13
+
14
+ .content
15
+ =yield
16
+
17
+ .footer
18
+ %p
19
+ This is the footer.
@@ -0,0 +1,99 @@
1
+ module RackConsole
2
+ class Ansi2Html
3
+ def initialize
4
+ @tag_b = { }
5
+ @tag_e = { }
6
+ end
7
+
8
+ def convert str, out = nil
9
+ @str = str.dup
10
+ @out = out || ''
11
+ @tags = [ ]
12
+ @out << %Q{<div class="ansi">}
13
+ scan!
14
+ tag_pop!
15
+ @out << %Q{</div>}
16
+ @out
17
+ end
18
+
19
+ def scan!
20
+ until @str.empty?
21
+ case @str
22
+ when /\A[^\e]+/
23
+ text($&)
24
+ when /\A\e\[([\d;]+)m/
25
+ codes = $1.split(';').reject(&:empty?).map(&:to_i)
26
+ codes.each do | code |
27
+ cls = CLASS_FOR_CODE[code]
28
+ tag(:span, cls) unless cls.nil?
29
+ end
30
+ when /\A.*/
31
+ text($&)
32
+ end
33
+ @str = $'
34
+ end
35
+ self
36
+ end
37
+
38
+ def tag name, cls
39
+ if cls
40
+ tag_b =
41
+ @tag_b[[name, cls]] ||= %Q{<#{name} class="#{cls}">}.freeze
42
+ tag_e =
43
+ @tag_e[name] ||= %Q{</#{name}>}.freeze
44
+ @tags << [ tag_b, tag_e ]
45
+ @out << tag_b
46
+ else
47
+ tag_pop!
48
+ end
49
+ end
50
+
51
+ def tag_pop!
52
+ while tag_be = @tags.pop
53
+ @out << tag_be[1]
54
+ end
55
+ end
56
+
57
+ def text str
58
+ lines = str.split("\n", 99999)
59
+ last = lines.pop
60
+ lines.each do | line |
61
+ @out << h(line)
62
+ @out << BR
63
+ end
64
+ @out << h(last)
65
+ end
66
+
67
+ def h(text)
68
+ Rack::Utils.escape_html(text.to_s).gsub(' ', '&nbsp;')
69
+ end
70
+
71
+ SPAN_END = "</span>".freeze
72
+ BR = "<br/>".freeze
73
+
74
+ # https://en.wikipedia.org/wiki/ANSI_escape_code
75
+ CLASS_FOR_CODE = {
76
+ 0 => false,
77
+ 1 => :bold,
78
+ 2 => :faint,
79
+ 3 => :italic,
80
+ 4 => :underline,
81
+ 30 => :black,
82
+ 31 => :red,
83
+ 32 => :green,
84
+ 33 => :yellow,
85
+ 34 => :blue,
86
+ 35 => :magenta,
87
+ 36 => :cyan,
88
+ 37 => :white,
89
+ 40 => :bg_black,
90
+ 41 => :bg_red,
91
+ 42 => :bg_green,
92
+ 43 => :bg_yellow,
93
+ 44 => :bg_blue,
94
+ 45 => :bg_magenta,
95
+ 46 => :bg_cyan,
96
+ 47 => :bg_white,
97
+ }
98
+ end
99
+ end
@@ -0,0 +1,57 @@
1
+ require 'rack_console/source_file'
2
+ require 'rack_console/app_helpers'
3
+ require 'sinatra'
4
+ require 'tilt/haml'
5
+ require 'haml'
6
+
7
+ module RackConsole
8
+ class App < Sinatra::Application
9
+ set :views, :default
10
+
11
+ get '/?' do
12
+ console!
13
+ end
14
+
15
+ post '/?' do
16
+ console!
17
+ end
18
+
19
+ get "/module/:expr" do
20
+ evaluate_module!
21
+ haml :'console/module', locals: locals, layout: layout
22
+ end
23
+
24
+ get "/method/:expr/:kind/:name" do
25
+ evaluate_method!
26
+ haml :'console/method', locals: locals, layout: layout
27
+ end
28
+
29
+ get "/methods/:owner/:kind/:name" do
30
+ evaluate_methods!
31
+ haml :'console/methods', locals: locals, layout: layout
32
+ end
33
+
34
+ get "/file/*" do
35
+ prepare_file!
36
+ haml :'console/file', locals: locals, layout: layout
37
+ end
38
+
39
+ get "/css/:path" do | path |
40
+ halt 404 if path =~ /\.\./
41
+ content_type 'text/css'
42
+ send_file "#{css_dir}/#{path}"
43
+ end
44
+
45
+ helpers do
46
+ include AppHelpers
47
+ end
48
+
49
+ def initialize config = { }
50
+ @config = config
51
+ @config[:views_default] ||= "#{File.expand_path('..', __FILE__)}/template/haml"
52
+ @config[:css_dir] ||= "#{File.expand_path('..', __FILE__)}/template/css"
53
+ super
54
+ end
55
+ attr_accessor :config
56
+ end
57
+ end
@@ -0,0 +1,418 @@
1
+ require 'active_support/core_ext/class/subclasses'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'pp'
4
+ require 'stringio'
5
+ require 'rack_console/ansi2html'
6
+
7
+ module RackConsole
8
+ module AppHelpers
9
+ def has_console_access?
10
+ true
11
+ end
12
+
13
+ def has_file_access? file
14
+ ! ! (file != '(eval)' && $".include?(file))
15
+ end
16
+
17
+ def url_root url
18
+ "#{config[:url_root_prefix]}#{url}"
19
+ end
20
+
21
+ def locals
22
+ @locals ||= { }
23
+ end
24
+
25
+ def layout
26
+ config[:layout] || :layout
27
+ end
28
+
29
+ def find_template(views, name, engine, &block)
30
+ views = config[:views] || views
31
+ Array(views).each do |v|
32
+ v = config[:views_default] if v == :default
33
+ super(v, name, engine, &block)
34
+ end
35
+ end
36
+
37
+ def css_dir
38
+ config[:css_dir]
39
+ end
40
+
41
+ ###############################
42
+
43
+ def console!
44
+ if has_console_access?
45
+ haml :console, locals: locals, layout: layout
46
+ else
47
+ raise "not authorized"
48
+ end
49
+ end
50
+
51
+ def evaluate_expr!
52
+ result_capture! do
53
+ @stdin = StringIO.new('')
54
+ @stdout = StringIO.new('')
55
+ @stderr = StringIO.new('')
56
+ @result_ok = false
57
+ @expr = (params[:expr] || '').strip
58
+ unless @expr.blank?
59
+ @result_evaled = true
60
+ Timeout.timeout(30) do
61
+ _stdin, _stdout, _stderr = $stdin, $stdout, $stderr
62
+ $stdin, $stdout, $stderr = @stdin, @stdout, @stderr
63
+ begin
64
+ expr_str = "begin; #{@expr} \n; end"
65
+ @result = eval(expr_str)
66
+ @result_ok = true
67
+ ensure
68
+ $stdin, $stdout, $stderr = _stdin, _stdout, _stderr
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ def evaluate_module!
76
+ evaluate_expr!
77
+ if @result_ok && @result.is_a?(Module)
78
+ result_capture! do
79
+ @module = @result
80
+ @ancestors = @module.ancestors.drop(1)
81
+ if @is_class = @module.is_a?(Class)
82
+ @superclass = @module.superclass
83
+ @subclasses = @module.subclasses.sort_by{|c| c.name || ''}
84
+ end
85
+ @constants = @module.constants(false).sort.map{|n| [ n, const_get_safe(@module, n) ]}
86
+ @methods = methods_for_module(@module)
87
+ end
88
+ end
89
+ end
90
+
91
+ def evaluate_method!
92
+ evaluate_expr!
93
+ if @result_ok && @result.is_a?(Module)
94
+ result_capture! do
95
+ @module = @result
96
+ @method_name = params[:name]
97
+ @method_kind = params[:kind].to_s =~ /i/ ? :instance_method : :method
98
+ @method = @module.send(@method_kind, @method_name) rescue nil
99
+ unless @method
100
+ @method = @module.send(:method, @method_name)
101
+ @method_kind = :method
102
+ end
103
+ @method_source_location = @method.source_location
104
+ @method_source = @method_source_location && SourceFile.new(@method_source_location).load!.narrow_to_block!
105
+ @result = @method
106
+ end
107
+ end
108
+ end
109
+
110
+ def evaluate_methods!
111
+ @methods = nil
112
+ result_capture! do
113
+ @methods = methods_matching(params)
114
+ end
115
+ end
116
+
117
+ def prepare_file!
118
+ path = params[:splat][0]
119
+ file, line = href_to_file_line(path)
120
+ result_capture! do
121
+ unless has_file_access? file
122
+ content_type 'text/plain'
123
+ return "NOT A LOADABLE FILE"
124
+ end
125
+ @source_file = SourceFile.new([ file, line ]).load!
126
+ end
127
+ end
128
+
129
+ def result_capture!
130
+ @result_ok = false
131
+ result = yield
132
+ @result_ok = true
133
+ result
134
+ rescue
135
+ @error = $!
136
+ @error_description = @error.inspect
137
+ ensure
138
+ @result_extended = @result.singleton_class.included_modules rescue nil
139
+ @result_class = @result.class.name
140
+ end
141
+
142
+ def format_object obj, inline = false
143
+ case obj
144
+ when Module
145
+ format_module obj
146
+ else
147
+ format_other obj, inline
148
+ end
149
+ end
150
+
151
+ def format_other obj, inline = false
152
+ if inline
153
+ literal_tag(h(limit_string(safe_format(obj), 80)))
154
+ else
155
+ safe_format_structured(obj)
156
+ end
157
+ end
158
+
159
+ def const_get_safe m, name
160
+ m.const_get(name)
161
+ rescue Object
162
+ "ERROR: #{$!.inspect}"
163
+ end
164
+
165
+ def format_source_location source_location, meth = nil, kind = nil, owner = nil
166
+ file, line = source_location
167
+ if file
168
+ "<a href='#{file_line_to_href file, line}' class='file_name'>#{file_name_tag("#{file}:#{line}")}</a>"
169
+ else
170
+ if meth
171
+ # Assume meth is Ruby Core and link to rdocs on ruby-doc.org.
172
+ name = meth.name
173
+ owner ||= meth.owner
174
+ owner_name = (owner.name || '').gsub('::', '/')
175
+ kind ||= (owner.instance_method(name) rescue nil) ? :i : :c
176
+ a_name = name.to_s.gsub(/([^a-z0-9_])/i){|m| "-%X" % [ m.ord ]}
177
+ a_name.sub!(/^-/, '')
178
+ a_name = "method-#{kind}-#{a_name}"
179
+ ruby_core_link = "http://www.ruby-doc.org/core-#{RUBY_VERSION}/#{owner_name}.html\##{a_name}"
180
+ "<a href='#{ruby_core_link}' class='ruby_core_doc'>#{h ruby_core_link}</a>"
181
+ else
182
+ "NONE"
183
+ end
184
+ end
185
+ end
186
+
187
+ def methods_matching params
188
+ name_p = match_pred(params[:name], :to_sym)
189
+ kind_p = match_pred(params[:kind], :to_sym)
190
+ owner_p = match_pred(params[:owner])
191
+
192
+ methods = [ ]
193
+ seen = { }
194
+ ObjectSpace.each_object(::Module) do | owner |
195
+ next unless (owner.name rescue nil)
196
+ next if owner_p && owner_p != owner.name
197
+ methods_for_module(owner, name_p, kind_p, seen, methods)
198
+ end
199
+ sort_methods! methods
200
+ methods
201
+ end
202
+
203
+ def match_pred value, m = nil
204
+ if value != '*' && value != ''
205
+ value = value.send(m) if m
206
+ else
207
+ value = nil
208
+ end
209
+ value
210
+ end
211
+
212
+ def methods_for_module owner, name_p = nil, kind_p = nil, seen = { }, to_methods = nil
213
+ methods = to_methods || [ ]
214
+ kind = :i
215
+ unless kind_p && kind_p != kind
216
+ instance_method_names(owner).each do | name |
217
+ next if name_p && name_p != (name = name.to_sym)
218
+ if meth = (owner.instance_method(name) rescue nil) and key = [ owner, kind, name ] and ! seen[key]
219
+ seen[key] = true
220
+ methods << MockMethod.new(meth, name, kind, owner)
221
+ end
222
+ end
223
+ end
224
+
225
+ kind = :c
226
+ unless kind_p && kind_p != kind
227
+ singleton_method_names(owner).each do | name |
228
+ next if name_p && name_p != (name = name.to_sym)
229
+ if meth = (owner.singleton_method(name) rescue nil) and key = [ owner, kind, name ] and ! seen[key]
230
+ seen[key] = true
231
+ methods << MockMethod.new(meth, name, kind, owner)
232
+ end
233
+ end
234
+ end
235
+ sort_methods! methods unless to_methods
236
+ methods
237
+ end
238
+
239
+ def sort_methods! methods
240
+ methods.sort_by!{|x| [ x.owner.to_s, x.kind, x.name ]}
241
+ end
242
+
243
+ def instance_method_names owner
244
+ ( owner.instance_methods(false) |
245
+ owner.private_instance_methods(false) |
246
+ owner.protected_instance_methods(false)
247
+ ).sort
248
+ end
249
+
250
+ def singleton_method_names owner
251
+ owner.singleton_methods(false)
252
+ end
253
+
254
+ class MockMethod
255
+ attr_accessor :meth, :name, :kind, :owner
256
+ def initialize *args
257
+ @meth, @name, @kind, @owner = args
258
+ end
259
+ def instance_method?
260
+ @kind == :i
261
+ end
262
+ def source_location
263
+ @meth.source_location
264
+ end
265
+ end
266
+
267
+ def file_line_to_href name, lineno = nil
268
+ link = name.sub(%r{^/}, '-')
269
+ link = link.split('/').map{|s| e s}.join('/')
270
+ link = url_root("/file/#{link}")
271
+ link << ":#{lineno}\##{lineno - 2}" if lineno
272
+ link
273
+ end
274
+
275
+ def href_to_file_line path
276
+ path.to_s =~ /^([^:]+)(:([^:]+))?/
277
+ file, line = $1, $3
278
+ file.sub!(/^-/, '/')
279
+ [ file, line && line.to_i ]
280
+ end
281
+
282
+ def source_file source_location
283
+ source_location && SourceFile.new(source_location).load!
284
+ end
285
+
286
+ def h(text)
287
+ Rack::Utils.escape_html(text.to_s)
288
+ end
289
+
290
+ def e(text)
291
+ Rack::Utils.escape(text.to_s)
292
+ end
293
+
294
+ def limit_string(text, len)
295
+ text = text.to_s
296
+ if text.size > len
297
+ text = text[0 .. len] + ' ...'
298
+ end
299
+ text
300
+ end
301
+
302
+ def format_module obj
303
+ return module_name_tag(h(obj.inspect)) unless obj && obj.name
304
+ path = obj.name.to_s.split('::')
305
+ result = ''
306
+ name = ''; pre = ''
307
+ path.each do | n |
308
+ name << n
309
+ href = url_root("/module/#{name}")
310
+ result << "<a href='#{href}' class='module_name'>#{module_name_tag("#{pre}#{h n}")}</a>"
311
+ name << '::'
312
+ pre = '::'
313
+ end
314
+ module_name_tag(result)
315
+ end
316
+
317
+ def format_method m, kind, owner = nil
318
+ owner ||= m.owner
319
+ source_location = m.source_location
320
+ source_location &&= source_location * ":"
321
+ href = method_href(m, kind, owner)
322
+ "<a href='#{href}' title='#{source_location}' class='method_name'>#{method_name_tag(h(m.name))}</a>"
323
+ end
324
+
325
+ def method_href m, kind, owner = nil
326
+ owner ||= m.owner
327
+ href = url_root("/method/#{owner.name}/#{e kind.to_s}/#{e m.name}")
328
+ end
329
+
330
+ def format_methods name
331
+ href = url_root("/methods/*/*/#{e name}")
332
+ "<a href='#{href}' title='Other methods named #{h name.inspect}' class='method_name'>#{method_name_tag(h(name))}</a>"
333
+ end
334
+
335
+ def file_name_tag str
336
+ %Q{<span class="file_name">#{str}</span>}
337
+ end
338
+
339
+ def module_name_tag str
340
+ %Q{<span class="module_name">#{str}</span>}
341
+ end
342
+
343
+ def method_name_tag str
344
+ %Q{<span class="method_name">#{str}</span>}
345
+ end
346
+
347
+ def literal_tag str
348
+ %Q{<span class="literal">#{str}</span>}
349
+ end
350
+
351
+ def format_backtrace line
352
+ line = line.to_s
353
+ html =
354
+ if line =~ /^(.*):(\d+):(in .*)$/ && File.exist?($1)
355
+ "#{format_source_location([$1, $2.to_i])}:#{h $3}"
356
+ else
357
+ file_name_tag(h line)
358
+ end
359
+ %Q{<span class="backtrace">#{html}</span>}
360
+ end
361
+
362
+ def wrap_lines str, width = 80
363
+ str.to_s.split("\n").map do | line |
364
+ wrap_line line, width
365
+ end * "\n"
366
+ end
367
+
368
+ def wrap_line str, width = 80
369
+ str = str.to_s
370
+ out = ''
371
+ pos = 0
372
+ while pos < str.size
373
+ out << h(str[pos, width])
374
+ pos += width
375
+ out << "&nbsp;\u21B5\n" if pos < str.size
376
+ end
377
+ out
378
+ end
379
+
380
+ def safe_format_structured obj
381
+ begin
382
+ if config[:awesome_print] && defined?(::AwesomePrint)
383
+ ansi = obj.ai(indent: 2, html: false, index: false)
384
+ ansi2html(ansi)
385
+ else
386
+ '<pre>' << wrap_lines(safe_pp(obj)) << '</pre>'
387
+ end
388
+ rescue
389
+ STDERR.puts " #{$!.inspect}: falling back to #inspect for #{obj.class}\n #{$!.backtrace * "\n "}"
390
+ '<pre>' << wrap_lines(obj.inspect) << '</pre>'
391
+ end
392
+ end
393
+
394
+ def safe_format obj
395
+ safe_pp(obj)
396
+ end
397
+
398
+ def safe_pp obj
399
+ ::PP.pp(obj, '')
400
+ rescue
401
+ STDERR.puts " #{$!.inspect}: falling back to #inspect for #{obj.class}\n #{$!.backtrace * "\n "}"
402
+ obj.inspect
403
+ end
404
+
405
+ def format_as_terminal str
406
+ str &&= str.to_s.force_encoding('UTF-8')
407
+ if str.blank?
408
+ %Q{<span class="none">NONE</span>}
409
+ else
410
+ ansi2html(str)
411
+ end
412
+ end
413
+
414
+ def ansi2html ansi
415
+ Ansi2Html.new.convert(ansi, '')
416
+ end
417
+ end
418
+ end
@@ -30,7 +30,7 @@ module RackConsole
30
30
  end
31
31
 
32
32
  def highlight_block!
33
- block_indent = nil
33
+ block_indent = last_line = nil
34
34
  @lines.each do | l |
35
35
  indent = (l[:str] =~ /^(\s*)\S/ && $1) || ''
36
36
  add_classes = block_finished = nil
@@ -42,6 +42,9 @@ module RackConsole
42
42
  block_indent = indent.size
43
43
  when block_indent && (indent.size > block_indent || l[:str] =~ /^(\s*)$/)
44
44
  add_classes = [ :block, :block_body ]
45
+ when block_indent && (indent.size < block_indent)
46
+ add_classes = nil
47
+ block_finished = true
45
48
  when block_indent && indent.size == block_indent
46
49
  if l[:str] =~ /^(\s*)(ensure|rescue)\b/
47
50
  add_classes = [ :block, :block_body ]
@@ -51,6 +54,7 @@ module RackConsole
51
54
  block_indent = nil
52
55
  block_finished = true
53
56
  end
57
+ add_classes = [ :block, :block_body ]
54
58
  end
55
59
  if add_classes
56
60
  l[:class].concat(add_classes).delete(:unselected_line)
@@ -0,0 +1,60 @@
1
+ .ansi .bold {
2
+ font-weight: bold;
3
+ }
4
+ .ansi .faint {
5
+ font-weight: lighter;
6
+ }
7
+ .ansi .italic {
8
+ font-style: italic;
9
+ }
10
+ .ansi .underline {
11
+ text-decoration: underline;
12
+ }
13
+ .ansi .black {
14
+ color: #000;
15
+ }
16
+ .ansi .red {
17
+ color: #f44;
18
+ }
19
+ .ansi .green {
20
+ color: #4f4;
21
+ }
22
+ .ansi .yellow {
23
+ color: #ff4;
24
+ }
25
+ .ansi .blue {
26
+ color: #66f;
27
+ }
28
+ .ansi .magenta {
29
+ color: #f4f;
30
+ }
31
+ .ansi .cyan {
32
+ color: #4ff;
33
+ }
34
+ .ansi .white {
35
+ color: #fff;
36
+ }
37
+ .ansi .bg_black {
38
+ background-color: #000;
39
+ }
40
+ .ansi .bg_red {
41
+ background-color: #f44;
42
+ }
43
+ .ansi .bg_green {
44
+ background-color: #4f4;
45
+ }
46
+ .ansi .bg_yellow {
47
+ background-color: #ff4;
48
+ }
49
+ .ansi .bg_blue {
50
+ background-color: #66f;
51
+ }
52
+ .ansi .bg_magenta {
53
+ background-color: #f4f;
54
+ }
55
+ .ansi .bg_cyan {
56
+ background-color: #4ff;
57
+ }
58
+ .ansi .bg_white {
59
+ background-color: #fff;
60
+ }
@@ -0,0 +1,144 @@
1
+ .rack_console {
2
+ border: 1px solid black;
3
+ padding: 1em;
4
+ font-family: sans-serif;
5
+ }
6
+
7
+ .rack_console a {
8
+ text-decoration: none;
9
+ }
10
+
11
+ .rack_console dt {
12
+ font-size: 80%;
13
+ color: #444;
14
+ }
15
+
16
+ .rack_console dd {
17
+ margin-left: 0.5em;
18
+ padding-bottom: 0.5em;
19
+ }
20
+
21
+ .rack_console .eval textarea.expr {
22
+ font-family: monospace;
23
+ font-size: 13px;
24
+ width: 95%;
25
+ height: 20em;
26
+ padding: 0.5em;
27
+ margin-left: 0.5em;
28
+ background-color: #000;
29
+ color: #fff;
30
+ }
31
+
32
+ .rack_console .result {
33
+ margin: 0px;
34
+ }
35
+
36
+ .rack_console .result pre {
37
+ font-family: monospace;
38
+ font-size: 13px;
39
+ background-color: #000;
40
+ color: #fff;
41
+ width: 95%;
42
+ margin-top: 0.5em;
43
+ padding: 0.5em;
44
+ }
45
+
46
+ .rack_console table {
47
+ border-spacing: 1px;
48
+ }
49
+
50
+ .rack_console .extended_by table tr td {
51
+ padding: 0px;
52
+ }
53
+
54
+ .rack_console .eval .io .result {
55
+ font-family: monospace;
56
+ font-size: 13px;
57
+ background-color: #000;
58
+ color: #fff;
59
+ width: 95%;
60
+ margin-top: 0.5em;
61
+ padding: 0.5em;
62
+ }
63
+
64
+ .rack_console span.module_name {
65
+ font-family: monospace;
66
+ color: #833;
67
+ }
68
+
69
+ .rack_console span.method_name {
70
+ font-family: monospace;
71
+ color: #383;
72
+ }
73
+
74
+ .rack_console span.file_name {
75
+ font-family: monospace;
76
+ color: #338;
77
+ }
78
+
79
+ .rack_console span.literal {
80
+ font-family: monospace;
81
+ color: #388;
82
+ }
83
+
84
+ .rack_console .source_listing {
85
+ font-family: monospace;
86
+ }
87
+
88
+ .rack_console .ruby_core_doc {
89
+ font-family: monospace;
90
+ color: #883;
91
+ }
92
+
93
+ .rack_console tr.constant {
94
+ vertical-align: top;
95
+ }
96
+
97
+ .rack_console .source_listing .unselected_line {
98
+ color: #888;
99
+ }
100
+
101
+ .rack_console .source_listing .selected_line {
102
+ font-weight: bold;
103
+ }
104
+
105
+ .rack_console .error dl.error {
106
+ border: 1px solid red;
107
+ padding: 0.5em;
108
+ margin: 0.5em;
109
+ }
110
+
111
+ .rack_console span.none {
112
+ font-style: italic;
113
+ font-size: 50%;
114
+ }
115
+
116
+ .rack_console .status {
117
+ font-style: italic;
118
+ font-size: 75%;
119
+ }
120
+
121
+ .rack_console .methods td.class {
122
+ text-align: right;
123
+ }
124
+
125
+ .rack_console .methods td.kind {
126
+ font-family: monospace;
127
+ }
128
+
129
+ .rack_console .methods td.source_location {
130
+ padding-left: 2em;
131
+ }
132
+
133
+ .rack_console .ansi {
134
+ width: 95%;
135
+ height: auto;
136
+ word-wrap: break-word;
137
+ overflow-x: auto;
138
+ font-family: monospace;
139
+ background-color: #000;
140
+ color: #fff;
141
+ margin-top: 0.5em;
142
+ padding: 0.5em;
143
+ }
144
+
@@ -0,0 +1,14 @@
1
+ .error
2
+ - if @error
3
+ %dl.error
4
+ %dt Class:
5
+ %dd.class=format_module(@error.class)
6
+ %dt Error:
7
+ %dd.description=literal_tag(h @error_description)
8
+ %dt Backtrace:
9
+ %dd.backtrace
10
+ %table.backtrace
11
+ - @error.backtrace.each do | bt |
12
+ %tr
13
+ %td=format_backtrace bt
14
+
@@ -0,0 +1,18 @@
1
+ .rack_console
2
+ .result
3
+ .file
4
+ %dl
5
+ - if @result_ok
6
+ %dt File:
7
+ %dd.file_name=file_name_tag(h @source_file.file)
8
+ %dt Source:
9
+ %dd.source
10
+ %table.source_listing
11
+ - @source_file.lines.each do | line |
12
+ %tr.source_line
13
+ %td{class: line[:class] * ' '}>
14
+ %a{name: line[:line]}>
15
+ %span.source_line=h("%4d %s" % [ line[:line], line[:str] ]).gsub(' ', '&nbsp;')
16
+
17
+ =haml :'console/error', locals: locals
18
+
@@ -0,0 +1,28 @@
1
+ .rack_console
2
+ - evaluate_method!
3
+ .result
4
+ .method
5
+ %dl
6
+ - if @result_evaled && @result_ok
7
+ %dt Class:
8
+ %dd= format_module(@method.class)
9
+ %dt Name:
10
+ %dd= format_methods(@method.name)
11
+ %dt Owner:
12
+ %dd= format_module(@module)
13
+ %dt Parameters:
14
+ %dd= h @method.parameters.inspect
15
+ %dt Arity:
16
+ %dd= h @method.arity
17
+ %dt Source Location:
18
+ %dd= format_source_location(@method_source_location, @method, nil, @module)
19
+ - if @method_source
20
+ %dt Source:
21
+ %dd
22
+ %table.source_listing
23
+ - @method_source.lines.each do | line |
24
+ %tr
25
+ %td{class: line[:class] * ' '}= h("%4d %s" % [ line[:line], line[:str] ]).gsub(' ', '&nbsp')
26
+
27
+ =haml :'console/error', locals: locals
28
+
@@ -0,0 +1,5 @@
1
+ .rack_console
2
+ .result
3
+ =haml :'console/methods_table', locals: locals.merge(methods: @methods)
4
+ =haml :'console/error', locals: locals
5
+
@@ -0,0 +1,11 @@
1
+ .methods
2
+ - if methods = locals[:methods]
3
+ %table
4
+ - @methods.each do | meth |
5
+ %tr.method
6
+ %td.class=format_module(meth.owner)
7
+ %td.kind
8
+ %a{href: method_href(meth, meth.kind, meth.owner), title: "Method detail"}=meth.instance_method? ? "#" : "."
9
+ %td.name=format_methods(meth.name)
10
+ %td.source_location=format_source_location(meth.source_location, meth, meth.kind)
11
+ .status{style: "text-align: center"}=h "#{methods.size} methods"
@@ -0,0 +1,60 @@
1
+ .rack_console
2
+ - evaluate_module!
3
+ .result
4
+ .module
5
+ %dl
6
+ - if @result_evaled && @result_ok
7
+ %dt Class:
8
+ %dd= format_module(@result.class)
9
+ %dt Result:
10
+ %dd= format_module(@result)
11
+ - if @is_class
12
+ %dt Superclass:
13
+ %dd= format_module(@superclass)
14
+ %dt Ancestors:
15
+ %dd
16
+ - if (@ancestors || [ ]).empty?
17
+ %span.none NONE
18
+ - else
19
+ %table.ancestors
20
+ - @ancestors.each do | mod |
21
+ %tr
22
+ %td= format_module(mod)
23
+ - if @subclasses
24
+ %dt Subclasses:
25
+ %dd
26
+ - if @subclasses.empty?
27
+ %span.none NONE
28
+ - else
29
+ %table.subclasses
30
+ - @subclasses.each do | mod |
31
+ %tr
32
+ %td= format_module(mod)
33
+ %dt Extended By:
34
+ %dd
35
+ - if (@result_extended || [ ]).empty?
36
+ %span.none NONE
37
+ - else
38
+ %table.extended_by
39
+ - @result_extended.each do | mod |
40
+ %tr
41
+ %td= format_module(mod)
42
+ %dt Constants:
43
+ %dd
44
+ - if (@constants || [ ]).empty?
45
+ %span.none NONE
46
+ - else
47
+ %table.constants
48
+ - @constants.each do | name, value |
49
+ %tr.constant
50
+ %td
51
+ %tt=h name
52
+ %td= format_module(value.class)
53
+ %td= format_object(value, :inline)
54
+ %dt
55
+ Methods:
56
+ %dd
57
+ = haml :'console/methods_table', locals: locals.merge(methods: @methods)
58
+
59
+ =haml :'console/error', locals: locals
60
+
@@ -0,0 +1,40 @@
1
+ .rack_console
2
+ .eval
3
+ - evaluate_expr!
4
+ %form{action: url_root("/"), method: "post"}
5
+ %div
6
+ %textarea.expr{name: 'expr'}=h @expr
7
+ %div
8
+ %input{ type: 'submit', value: 'Evaluate' }
9
+ .result
10
+ - if @result_evaled && @result_ok
11
+ %dl
12
+ %dt Class:
13
+ %dd
14
+ .class= format_module(@result.class)
15
+ %dt Result:
16
+ %dd
17
+ .result
18
+ ~ format_object(@result)
19
+ %dt Extended By:
20
+ %dd
21
+ .extended_by
22
+ - extensions = (@result_extended || [ ])
23
+ - if extensions.empty?
24
+ %span.none NONE
25
+ - else
26
+ %table
27
+ - extensions.each do | mod |
28
+ %tr
29
+ %td= format_module(mod)
30
+ %dt $stdout:
31
+ %dd
32
+ .io.stdout
33
+ ~ format_as_terminal(@stdout && @stdout.string)
34
+ %dt $stderr:
35
+ %dd
36
+ .io.stderr
37
+ ~ format_as_terminal(@stderr && @stderr.string)
38
+
39
+ =haml :'console/error', locals: locals
40
+
@@ -0,0 +1,18 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title rack_console
5
+ %link(rel="stylesheet" href="#{url_root}/css/rack_console.css")
6
+ %link(rel="stylesheet" href="#{url_root}/css/ansi.css")
7
+ %body
8
+ .wrapper
9
+ .header
10
+ %h1 rack_console
11
+ %h2 An interactive Ruby console as a Rack App.
12
+
13
+ .content
14
+ =yield
15
+
16
+ .footer
17
+ %p
18
+ This is the footer.
@@ -1,3 +1,3 @@
1
1
  module RackConsole
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.1"
3
3
  end
data/rack_console.gemspec CHANGED
@@ -27,6 +27,11 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
29
 
30
+ spec.add_dependency "sinatra", "~> 1.4"
31
+ spec.add_dependency "haml", "~> 4.0"
32
+ spec.add_dependency "activesupport", "~> 4.2"
33
+ spec.add_dependency "awesome_print", "~> 1.6"
34
+
30
35
  spec.add_development_dependency "bundler", "~> 1.10"
31
36
  spec.add_development_dependency "rake", "~> 10.0"
32
37
  spec.add_development_dependency "rspec", "~> 3.3"
metadata CHANGED
@@ -1,15 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack_console
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kurt Stephens
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-06-18 00:00:00.000000000 Z
11
+ date: 2015-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sinatra
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: haml
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: awesome_print
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
13
69
  - !ruby/object:Gem::Dependency
14
70
  name: bundler
15
71
  requirement: !ruby/object:Gem::Requirement
@@ -111,8 +167,25 @@ files:
111
167
  - Rakefile
112
168
  - bin/console
113
169
  - bin/setup
170
+ - example/ex01/app.rb
171
+ - example/ex01/config.ru
172
+ - example/ex01/template/haml/index.haml
173
+ - example/ex01/template/haml/layout.haml
114
174
  - lib/rack_console.rb
175
+ - lib/rack_console/ansi2html.rb
176
+ - lib/rack_console/app.rb
177
+ - lib/rack_console/app_helpers.rb
115
178
  - lib/rack_console/source_file.rb
179
+ - lib/rack_console/template/css/ansi.css
180
+ - lib/rack_console/template/css/rack_console.css
181
+ - lib/rack_console/template/haml/console.haml
182
+ - lib/rack_console/template/haml/console/error.haml
183
+ - lib/rack_console/template/haml/console/file.haml
184
+ - lib/rack_console/template/haml/console/method.haml
185
+ - lib/rack_console/template/haml/console/methods.haml
186
+ - lib/rack_console/template/haml/console/methods_table.haml
187
+ - lib/rack_console/template/haml/console/module.haml
188
+ - lib/rack_console/template/haml/layout.haml
116
189
  - lib/rack_console/version.rb
117
190
  - rack_console.gemspec
118
191
  homepage: https://github.com/kstephens/rack_console
@@ -135,7 +208,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
208
  version: '0'
136
209
  requirements: []
137
210
  rubyforge_project:
138
- rubygems_version: 2.4.3
211
+ rubygems_version: 2.2.2
139
212
  signing_key:
140
213
  specification_version: 4
141
214
  summary: A Rack App that provides a basic interactive Ruby console and introspection.