rack_console 0.3.2 → 0.4.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: 4a4ba0df0574adddc3056759da9cd1f0977e45bd
4
- data.tar.gz: 3ec32e29c2b3ff8145767f5e3ea4b26b7bfe433d
3
+ metadata.gz: 8dfdd6cd65e2383e4704cdf422bb10cdd53ccfd5
4
+ data.tar.gz: ba1b0816dfe7c7d16e2eb68f1e97f05a6ba511fa
5
5
  SHA512:
6
- metadata.gz: 9aa66a2d07385e9ae6431a428eb88acc8c17a4b2050102ce034c0027dd9abb9a0c6877d2a9314d54628985f45be6d18d7f4978db2f4815446a79e09ca4ce81fa
7
- data.tar.gz: 15516fe338a8c8b83ff56fa8a0cdb82d1a3bd8a24ba35b3b6d5ffaf98dd0d8af4dabaa03d304a9fa49af60ea536e47d68d755d31e85aa0485ed891aef9f35d03
6
+ metadata.gz: 0b6370d265af9ec4fb74841c59c2d17925daa8d46d8fac04ce583bd688625e68ba250f711fdf1417108d264d0dfc4d0f60e0aa611dcd6d210f188c929e9946da
7
+ data.tar.gz: e3f1e23ceb918f495c83ca4896b33e8a5840f5354ecefefba84c8124b968cc7161b15c42c8cce153aef832bbac2b7f280d74396d8f00b4b250cb9244894f1139
data/Rakefile CHANGED
@@ -4,3 +4,12 @@ require "rspec/core/rake_task"
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  task :default => :spec
7
+
8
+ task :coverage => [ :enable_coverage, :spec ]
9
+ task :enable_coverage do
10
+ ENV['COVERAGE'] = "1"
11
+ end
12
+
13
+ task :run do
14
+ system "cd example/ex01 && exec bundle exec rackup -p 9999"
15
+ end
@@ -2,9 +2,11 @@ $:.unshift "../../lib"
2
2
  $:.unshift "."
3
3
  require 'rack_console/app'
4
4
  require 'app'
5
+ require 'ostruct'
5
6
  use Rack::Reloader
6
7
  run Rack::URLMap.new(
7
8
  "/console" => RackConsole::App.new(
9
+ eval_target: OpenStruct.new,
8
10
  awesome_print: true,
9
11
  url_root_prefix: "/console",
10
12
  views: [ 'template/haml', :default ]),
@@ -2,18 +2,17 @@
2
2
  %html
3
3
  %head
4
4
  %title rack_console
5
- %link(rel="stylesheet" href="/console/css/rack_console.css")
6
- %link(rel="stylesheet" href="/console/css/ansi.css")
5
+ %link{rel: "stylesheet", href: url_root("/css/rack_console.css")}
6
+ %link{rel: "stylesheet", href: url_root("/css/ansi.css")}
7
7
  %body
8
- .wrapper
9
- .header
10
- %h1
11
- %a{href: "/console"}rack_console
12
- %h2 An interactive Ruby console as a Rack App.
8
+ .rack_console
9
+ .wrapper
10
+ .header
11
+ %h1
12
+ %a{href:"/console"}rack_console
13
+ %p Test layout
14
+ .content
15
+ =yield
13
16
 
14
- .content
15
- =yield
16
-
17
- .footer
18
- %p
19
- This is the footer.
17
+ .footer
18
+ %hr
@@ -2,8 +2,7 @@ require 'rack/utils'
2
2
 
3
3
  module RackConsole
4
4
  class Ansi2Html
5
- @@tag_b = { }
6
- @@tag_e = { }
5
+ @@tag_cache = { }
7
6
 
8
7
  def self.convert str, out = nil
9
8
  new.convert(str, out)
@@ -41,12 +40,14 @@ module RackConsole
41
40
 
42
41
  def tag name, cls
43
42
  if cls
44
- tag_b =
45
- @@tag_b[[name, cls]] ||= %Q{<#{name} class="#{cls}">}.freeze
46
- tag_e =
47
- @@tag_e[name] ||= %Q{</#{name}>}.freeze
48
- @tags << [ tag_b, tag_e ]
49
- @out << tag_b
43
+ tag_be =
44
+ (@@tag_cache[name] ||= { })[cls] ||=
45
+ [
46
+ %Q{<#{name} class="#{cls}">}.freeze,
47
+ %Q{</#{name}>}.freeze,
48
+ ].freeze
49
+ @tags << tag_be
50
+ @out << tag_be[0]
50
51
  else
51
52
  tag_pop!
52
53
  end
@@ -9,52 +9,42 @@ module RackConsole
9
9
  class App < Sinatra::Application
10
10
  set :views, :default
11
11
 
12
+ before do
13
+ check_access! unless request.path_info =~ %r{^/css/}
14
+ end
15
+
12
16
  get '/?' do
13
- with_access do
14
- console!
15
- end
17
+ console!
16
18
  end
17
19
 
18
20
  post '/?' do
19
- with_access do
20
- console!
21
- end
21
+ console!
22
22
  end
23
23
 
24
24
  get "/module/:expr" do
25
- with_access do
26
- evaluate_module!
27
- haml :'console/module', locals: locals, layout: layout
28
- end
25
+ evaluate_module!
26
+ haml :'console/module', locals: locals, layout: layout
29
27
  end
30
28
 
31
29
  get "/method/:expr/:kind/:name" do
32
- with_access do
33
- evaluate_method!
34
- haml :'console/method', locals: locals, layout: layout
35
- end
30
+ evaluate_method!
31
+ haml :'console/method', locals: locals, layout: layout
36
32
  end
37
33
 
38
34
  get "/methods/:owner/:kind/:name" do
39
- with_access do
40
- evaluate_methods!
41
- haml :'console/methods', locals: locals, layout: layout
42
- end
35
+ evaluate_methods!
36
+ haml :'console/methods', locals: locals, layout: layout
43
37
  end
44
38
 
45
39
  get "/file/*" do
46
- with_access do
47
- prepare_file!
48
- haml :'console/file', locals: locals, layout: layout
49
- end
40
+ prepare_file!
41
+ haml :'console/file', locals: locals, layout: layout
50
42
  end
51
43
 
52
44
  get "/methods/file/*" do
53
- with_access do
54
- prepare_file!
55
- @methods = methods_within_file(@source_file.file) if @source_file
56
- haml :'console/methods', locals: locals, layout: layout
57
- end
45
+ prepare_file!
46
+ @methods = methods_within_file(@source_file.file) if @source_file
47
+ haml :'console/methods', locals: locals, layout: layout
58
48
  end
59
49
 
60
50
  get "/css/:path" do | path |
@@ -1,55 +1,14 @@
1
1
  require 'active_support/core_ext/class/subclasses'
2
2
  require 'active_support/core_ext/object/blank'
3
- require 'pp'
4
3
  require 'stringio'
5
- require 'rack_console/ansi2html'
6
4
  require 'rack_console/expr_helpers'
7
- require 'rack_console/mock_method'
5
+ require 'rack_console/configuration'
6
+ require 'rack_console/method_introspection'
7
+ require 'rack_console/formatting'
8
8
 
9
9
  module RackConsole
10
10
  module AppHelpers
11
- include ExprHelpers
12
-
13
- def has_console_access?
14
- case a = config[:authorized?]
15
- when true, false
16
- a
17
- when Proc
18
- a.call
19
- else
20
- true
21
- end
22
- end
23
-
24
- def has_file_access? file
25
- ! ! (file != '(eval)' && $".include?(file))
26
- end
27
-
28
- def url_root url
29
- "#{config[:url_root_prefix]}#{url}"
30
- end
31
-
32
- def locals
33
- @locals ||= { }
34
- end
35
-
36
- def layout
37
- config[:layout] || :layout
38
- end
39
-
40
- def find_template(views, name, engine, &block)
41
- views = config[:views] || views
42
- Array(views).each do |v|
43
- v = config[:views_default] if v == :default
44
- super(v, name, engine, &block)
45
- end
46
- end
47
-
48
- def css_dir
49
- config[:css_dir]
50
- end
51
-
52
- ###############################
11
+ include ExprHelpers, Configuration, MethodIntrospection, Formatting
53
12
 
54
13
  def with_access
55
14
  if has_console_access?
@@ -75,8 +34,7 @@ module RackConsole
75
34
  @result_evaled = true
76
35
  Timeout.timeout(config[:eval_timeout] || 120) do
77
36
  capture_stdio! do
78
- expr_str = "begin; #{@expr} \n; end"
79
- @result = eval(expr_str)
37
+ @result = eval_expr(eval_target, @expr)
80
38
  @result_ok = true
81
39
  end
82
40
  end
@@ -84,6 +42,18 @@ module RackConsole
84
42
  end
85
43
  end
86
44
 
45
+ def eval_expr et, expr
46
+ expr_str = "begin; #{@expr} \n; end"
47
+ case et
48
+ when nil, false
49
+ eval(expr_str)
50
+ when Module
51
+ et.module_eval(expr_str)
52
+ else
53
+ et.instance_eval(expr_str)
54
+ end
55
+ end
56
+
87
57
  def evaluate_module!
88
58
  evaluate_expr!
89
59
  @show_stdio = @show_result = false
@@ -172,310 +142,11 @@ module RackConsole
172
142
  end
173
143
  end
174
144
 
175
- def server_info
176
- thr = Thread.current
177
- (config[:server_info] || { }).merge(
178
- host: Socket.gethostname,
179
- pid: Process.pid,
180
- ppid: Process.ppid,
181
- thread: thr[:name] || thr.object_id,
182
- )
183
- end
184
-
185
- def format_object obj, inline = false
186
- case obj
187
- when Module
188
- format_module obj
189
- else
190
- format_other obj, inline
191
- end
192
- end
193
-
194
- def format_other obj, inline = false
195
- if inline
196
- literal_tag(h(limit_string(safe_format(obj), 80)))
197
- else
198
- safe_format_structured(obj)
199
- end
200
- end
201
-
202
145
  def const_get_safe m, name
203
146
  m.const_get(name)
204
147
  rescue Object
205
148
  "ERROR: #{$!.inspect}"
206
149
  end
207
150
 
208
- def format_source_location source_location, meth = nil, kind = nil, owner = nil
209
- file, line = source_location
210
- if file
211
- "<a href='#{file_line_to_href file, line}' class='file_name'>#{file_name_tag("#{file}:#{line}")}</a>"
212
- else
213
- if meth
214
- # Assume meth is Ruby Core and link to rdocs on ruby-doc.org.
215
- name = meth.name
216
- owner ||= meth.owner
217
- owner_name = (owner.name || '').gsub('::', '/')
218
- kind ||= (owner.instance_method(name) rescue nil) ? :i : :c
219
- a_name = name.to_s.gsub(/([^a-z0-9_])/i){|m| "-%X" % [ m.ord ]}
220
- a_name.sub!(/^-/, '')
221
- a_name = "method-#{kind}-#{a_name}"
222
- ruby_core_link = "http://www.ruby-doc.org/core-#{RUBY_VERSION}/#{owner_name}.html\##{a_name}"
223
- "<a href='#{ruby_core_link}' class='ruby_core_doc'>#{h ruby_core_link}</a>"
224
- else
225
- "NONE"
226
- end
227
- end
228
- end
229
-
230
- def methods_matching params
231
- name_p = match_pred(params[:name], :to_sym)
232
- kind_p = match_pred(params[:kind], :to_sym)
233
- owner_p = match_pred(params[:owner])
234
- file_p = match_pred(params[:file])
235
-
236
- methods = [ ]
237
- seen = { }
238
- ObjectSpace.each_object(::Module) do | owner |
239
- next unless (owner.name rescue nil)
240
- next if owner_p && owner_p != owner.name
241
- methods_for_module(owner, name_p, kind_p, file_p, seen, methods)
242
- end
243
- sort_methods! methods
244
- methods
245
- end
246
-
247
- def match_pred value, m = nil
248
- if value != nil && value != '*' && value != ''
249
- value = value.send(m) if m
250
- else
251
- value = nil
252
- end
253
- value
254
- end
255
-
256
- def methods_for_module owner, name_p = nil, kind_p = nil, file_p = nil, seen = { }, to_methods = nil
257
- methods = to_methods || [ ]
258
- kind = :i
259
- unless kind_p && kind_p != kind
260
- instance_method_names(owner).each do | name |
261
- next if name_p && name_p != (name = name.to_sym)
262
- if meth = (owner.instance_method(name) rescue nil) and key = [ owner, kind, name ] and ! seen[key]
263
- seen[key] = true
264
- if file_p
265
- f = meth.source_location and f = f.first
266
- next if f != file_p
267
- end
268
- methods << MockMethod.new(meth, name, kind, owner)
269
- end
270
- end
271
- end
272
-
273
- kind = :c
274
- unless kind_p && kind_p != kind
275
- singleton_method_names(owner).each do | name |
276
- next if name_p && name_p != (name = name.to_sym)
277
- if meth = (owner.singleton_method(name) rescue nil) and key = [ owner, kind, name ] and ! seen[key]
278
- seen[key] = true
279
- if file_p
280
- f = meth.source_location and f = f.first
281
- next if f != file_p
282
- end
283
- methods << MockMethod.new(meth, name, kind, owner)
284
- end
285
- end
286
- end
287
- sort_methods! methods unless to_methods
288
- methods
289
- end
290
-
291
- def sort_methods! methods
292
- methods.sort_by!{|x| [ x.owner.to_s, x.kind, x.name ]}
293
- end
294
-
295
- def instance_method_names owner
296
- ( owner.instance_methods(false) |
297
- owner.private_instance_methods(false) |
298
- owner.protected_instance_methods(false)
299
- ).sort
300
- end
301
-
302
- def singleton_method_names owner
303
- owner.singleton_methods(false)
304
- end
305
-
306
- def methods_within_file file
307
- methods = methods_matching(file: file)
308
- sort_methods_by_source_location! methods
309
- end
310
-
311
- def sort_methods_by_source_location! methods
312
- methods.sort_by!{|x| x.source_location || DUMMY_SOURCE_LOCATION }
313
- end
314
- DUMMY_SOURCE_LOCATION = [ "".freeze, 0 ].freeze
315
-
316
- def file_line_to_href name, lineno = nil
317
- link = name.sub(%r{^/}, '-')
318
- link = link.split('/').map{|s| e s}.join('/')
319
- link = url_root("/file/#{link}")
320
- link << ":#{lineno}\##{lineno - 2}" if lineno
321
- link
322
- end
323
-
324
- def href_to_file_line path
325
- path.to_s =~ /^([^:]+)(:([^:]+))?/
326
- file, line = $1, $3
327
- file.sub!(/^-/, '/')
328
- [ file, line && line.to_i ]
329
- end
330
-
331
- def source_file_methods_href file
332
- link = file.sub(%r{^/}, '-')
333
- link = url_root("/methods/file/#{link}")
334
- end
335
-
336
- def source_file source_location
337
- source_location && SourceFile.new(source_location).load!
338
- end
339
-
340
- def h(text)
341
- Rack::Utils.escape_html(text.to_s)
342
- end
343
-
344
- def e(text)
345
- Rack::Utils.escape(text.to_s)
346
- end
347
-
348
- def limit_string(text, len)
349
- text = text.to_s
350
- if text.size > len
351
- text = text[0 .. len] + ' ...'
352
- end
353
- text
354
- end
355
-
356
- def format_module obj
357
- return module_name_tag(h(obj.inspect)) unless obj && obj.name
358
- path = obj.name.to_s.split('::')
359
- result = ''
360
- name = ''; pre = ''
361
- path.each do | n |
362
- name << n
363
- href = url_root("/module/#{name}")
364
- result << "<a href='#{href}' class='module_name'>#{module_name_tag("#{pre}#{h n}")}</a>"
365
- name << '::'
366
- pre = '::'
367
- end
368
- module_name_tag(result)
369
- end
370
-
371
- def format_method m, kind, owner = nil
372
- owner ||= m.owner
373
- source_location = m.source_location
374
- source_location &&= source_location * ":"
375
- href = method_href(m, kind, owner)
376
- "<a href='#{href}' title='#{source_location}' class='method_name'>#{method_name_tag(h(m.name))}</a>"
377
- end
378
-
379
- def method_href m, kind, owner = nil
380
- owner ||= m.owner
381
- href = url_root("/method/#{owner.name}/#{e kind.to_s}/#{e m.name}")
382
- end
383
-
384
- def format_methods obj
385
- case obj
386
- when nil
387
- return nil
388
- when ::Method, ::UnboundMethod, MockMethod
389
- name = obj.name or return nil
390
- else
391
- name = obj.to_s
392
- end
393
- href = url_root("/methods/*/*/#{e name}")
394
- "<a href='#{href}' title='Other methods named #{h name.inspect}' class='method_name'>#{method_name_tag(h(name))}</a>"
395
- end
396
-
397
- def file_name_tag str
398
- %Q{<span class="file_name">#{str}</span>}
399
- end
400
-
401
- def module_name_tag str
402
- %Q{<span class="module_name">#{str}</span>}
403
- end
404
-
405
- def method_name_tag str
406
- %Q{<span class="method_name">#{str}</span>}
407
- end
408
-
409
- def literal_tag str
410
- %Q{<span class="literal">#{str}</span>}
411
- end
412
-
413
- def format_backtrace line
414
- line = line.to_s
415
- html =
416
- if line =~ /^(.*):(\d+):(in .*)$/ && File.exist?($1)
417
- "#{format_source_location([$1, $2.to_i])}:#{h $3}"
418
- else
419
- file_name_tag(h line)
420
- end
421
- %Q{<span class="backtrace">#{html}</span>}
422
- end
423
-
424
- def wrap_lines str, width = nil
425
- str.to_s.split("\n").map do | line |
426
- wrap_line line, width
427
- end * "\n"
428
- end
429
-
430
- def wrap_line str, width = nil
431
- width ||= config[:wrap_width] || 80
432
- str = str.to_s
433
- out = ''
434
- pos = 0
435
- while pos < str.size
436
- out << h(str[pos, width])
437
- pos += width
438
- out << "&nbsp;\u21B5\n" if pos < str.size
439
- end
440
- out
441
- end
442
-
443
- def safe_format_structured obj
444
- begin
445
- if config[:awesome_print] && defined?(::AwesomePrint)
446
- ansi = obj.ai(indent: 2, html: false, index: false)
447
- ansi2html(ansi)
448
- else
449
- '<pre>' << wrap_lines(safe_pp(obj)) << '</pre>'
450
- end
451
- rescue
452
- STDERR.puts " #{$!.inspect}: falling back to #inspect for #{obj.class}\n #{$!.backtrace * "\n "}"
453
- '<pre>' << wrap_lines(obj.inspect) << '</pre>'
454
- end
455
- end
456
-
457
- def safe_format obj
458
- safe_pp(obj)
459
- end
460
-
461
- def safe_pp obj
462
- ::PP.pp(obj, '')
463
- rescue
464
- STDERR.puts " #{$!.inspect}: falling back to #inspect for #{obj.class}\n #{$!.backtrace * "\n "}"
465
- obj.inspect
466
- end
467
-
468
- def format_as_terminal str
469
- str &&= str.to_s.force_encoding('UTF-8')
470
- if str.blank?
471
- %Q{<span class="none">NONE</span>}
472
- else
473
- ansi2html(str)
474
- end
475
- end
476
-
477
- def ansi2html ansi
478
- Ansi2Html.new.convert(ansi, '')
479
- end
480
151
  end
481
152
  end
@@ -0,0 +1,74 @@
1
+ require 'time'
2
+ require 'date'
3
+ require 'rack_console/mock_method'
4
+
5
+ module RackConsole
6
+ module Configuration
7
+ def has_console_access?
8
+ case a = config[:authorized?]
9
+ when true, false
10
+ a
11
+ when Proc
12
+ a.call
13
+ else
14
+ true
15
+ end
16
+ end
17
+
18
+ def check_access!
19
+ unauthorized! unless has_console_access?
20
+ end
21
+
22
+ def unauthorized!
23
+ raise Error, "not authorized"
24
+ end
25
+
26
+ def has_file_access? file
27
+ ! ! (file != '(eval)' && $".include?(file))
28
+ end
29
+
30
+ def url_root url
31
+ "#{config[:url_root_prefix]}#{url}"
32
+ end
33
+
34
+ def locals
35
+ @locals ||= { }
36
+ end
37
+
38
+ def layout
39
+ config[:layout] || :layout
40
+ end
41
+
42
+ def find_template(views, name, engine, &block)
43
+ views = config[:views] || views
44
+ Array(views).each do |v|
45
+ v = config[:views_default] if v == :default
46
+ super(v, name, engine, &block)
47
+ end
48
+ end
49
+
50
+ def css_dir
51
+ config[:css_dir]
52
+ end
53
+
54
+ def eval_target
55
+ case et = config[:eval_target]
56
+ when Proc
57
+ et.call
58
+ else
59
+ et
60
+ end
61
+ end
62
+
63
+ def server_info
64
+ thr = Thread.current
65
+ (config[:server_info] || { }).merge(
66
+ host: Socket.gethostname,
67
+ pid: Process.pid,
68
+ ppid: Process.ppid,
69
+ thread: thr[:name] || thr.object_id,
70
+ )
71
+ end
72
+ end
73
+ end
74
+
@@ -15,7 +15,7 @@ module RackConsole
15
15
 
16
16
  def expr_for_object obj, mod = nil, kind = nil
17
17
  case obj
18
- when nil, true, false, ::Numeric, ::String
18
+ when nil, true, false, ::Numeric, ::String, ::Symbol
19
19
  obj.inspect
20
20
  when ::Time
21
21
  "Time.parse(#{obj.iso8601(6).inspect})"
@@ -0,0 +1,214 @@
1
+ # encoding: UTF-8
2
+ require 'time'
3
+ require 'date'
4
+ require 'pp'
5
+ require 'rack_console/ansi2html'
6
+
7
+ module RackConsole
8
+ module Formatting
9
+ def format_object obj, inline = false
10
+ case obj
11
+ when Module
12
+ format_module obj
13
+ else
14
+ format_other obj, inline
15
+ end
16
+ end
17
+
18
+ def format_other obj, inline = false
19
+ if inline
20
+ literal_tag(h(limit_string(safe_format(obj).strip, 80)))
21
+ else
22
+ safe_format_structured(obj)
23
+ end
24
+ end
25
+
26
+ def format_source_location source_location, meth = nil, kind = nil, owner = nil
27
+ file, line = source_location
28
+ if file
29
+ "<a href='#{file_line_to_href file, line}' class='file_name'>#{file_name_tag("#{file}:#{line}")}</a>"
30
+ else
31
+ if meth
32
+ # Assume meth is Ruby Core and link to rdocs on ruby-doc.org.
33
+ name = meth.name
34
+ owner ||= meth.owner
35
+ owner_name = (owner.name || '').gsub('::', '/')
36
+ kind ||= (owner.instance_method(name) rescue nil) ? :i : :c
37
+ a_name = name.to_s.gsub(/([^a-z0-9_])/i){|m| "-%X" % [ m.ord ]}
38
+ a_name.sub!(/^-/, '')
39
+ a_name = "method-#{kind}-#{a_name}"
40
+ ruby_core_link = "http://www.ruby-doc.org/core-#{RUBY_VERSION}/#{owner_name}.html\##{a_name}"
41
+ "<a href='#{ruby_core_link}' class='ruby_core_doc'>#{h ruby_core_link}</a>"
42
+ else
43
+ "NONE"
44
+ end
45
+ end
46
+ end
47
+
48
+ def file_line_to_href name, lineno = nil
49
+ link = name.sub(%r{^/}, '-')
50
+ link = link.split('/').map{|s| e s}.join('/')
51
+ link = url_root("/file/#{link}")
52
+ link << ":#{lineno}\##{lineno - 2}" if lineno
53
+ link
54
+ end
55
+
56
+ def href_to_file_line path
57
+ path.to_s =~ /^([^:]+)(:([^:]+))?/
58
+ file, line = $1, $3
59
+ file.sub!(/^-/, '/')
60
+ [ file, line && line.to_i ]
61
+ end
62
+
63
+ def source_file_methods_href file
64
+ link = file.sub(%r{^/}, '-')
65
+ link = url_root("/methods/file/#{link}")
66
+ end
67
+
68
+ def source_file source_location
69
+ source_location && SourceFile.new(source_location).load!
70
+ end
71
+
72
+ def h(text)
73
+ Rack::Utils.escape_html(text.to_s)
74
+ end
75
+
76
+ def e(text)
77
+ Rack::Utils.escape(text.to_s)
78
+ end
79
+
80
+ def limit_string(text, len)
81
+ text = text.to_s
82
+ if text.size > len
83
+ text = text[0 .. len] + ' ...'
84
+ end
85
+ text
86
+ end
87
+
88
+ def format_module obj
89
+ return module_name_tag(h(obj.inspect)) unless obj && obj.name
90
+ path = obj.name.to_s.split('::')
91
+ result = ''
92
+ name = ''; pre = ''
93
+ path.each do | n |
94
+ name << n
95
+ href = url_root("/module/#{name}")
96
+ result << "<a href='#{href}' class='module_name'>#{module_name_tag("#{pre}#{h n}")}</a>"
97
+ name << '::'
98
+ pre = '::'
99
+ end
100
+ module_name_tag(result)
101
+ end
102
+
103
+ def format_method m, kind, owner = nil
104
+ owner ||= m.owner
105
+ source_location = m.source_location
106
+ source_location &&= source_location * ":"
107
+ href = method_href(m, kind, owner)
108
+ "<a href='#{href}' title='#{source_location}' class='method_name'>#{method_name_tag(h(m.name))}</a>"
109
+ end
110
+
111
+ def method_href m, kind, owner = nil
112
+ owner ||= m.owner
113
+ href = url_root("/method/#{owner.name}/#{e kind.to_s}/#{e m.name}")
114
+ end
115
+
116
+ def format_methods obj
117
+ case obj
118
+ when nil
119
+ return nil
120
+ when ::Method, ::UnboundMethod, MockMethod
121
+ name = obj.name or return nil
122
+ else
123
+ name = obj.to_s
124
+ end
125
+ href = url_root("/methods/*/*/#{e name}")
126
+ "<a href='#{href}' title='Other methods named #{h name.inspect}' class='method_name'>#{method_name_tag(h(name))}</a>"
127
+ end
128
+
129
+ def file_name_tag str
130
+ %Q{<span class="file_name">#{str}</span>}
131
+ end
132
+
133
+ def module_name_tag str
134
+ %Q{<span class="module_name">#{str}</span>}
135
+ end
136
+
137
+ def method_name_tag str
138
+ %Q{<span class="method_name">#{str}</span>}
139
+ end
140
+
141
+ def literal_tag str
142
+ %Q{<span class="literal">#{str}</span>}
143
+ end
144
+
145
+ def format_backtrace line
146
+ line = line.to_s
147
+ html =
148
+ if line =~ /^(.*):(\d+):(in .*)$/ && File.exist?($1)
149
+ "#{format_source_location([$1, $2.to_i])}:#{h $3}"
150
+ else
151
+ file_name_tag(h line)
152
+ end
153
+ %Q{<span class="backtrace">#{html}</span>}
154
+ end
155
+
156
+ def wrap_lines str, width = nil
157
+ str.to_s.split("\n").map do | line |
158
+ wrap_line line, width
159
+ end * "\n"
160
+ end
161
+
162
+ def wrap_line str, width = nil
163
+ width ||= config[:wrap_width] || 80
164
+ str = str.to_s
165
+ out = ''
166
+ pos = 0
167
+ while pos < str.size
168
+ out << h(str[pos, width])
169
+ pos += width
170
+ out << "&nbsp;\u21B5\n" if pos < str.size
171
+ end
172
+ out
173
+ end
174
+
175
+ def safe_format_structured obj
176
+ begin
177
+ if config[:awesome_print] && defined?(::AwesomePrint)
178
+ ansi = obj.ai(indent: 2, html: false, index: false)
179
+ ansi2html(ansi)
180
+ else
181
+ '<pre>' << wrap_lines(safe_pp(obj)) << '</pre>'
182
+ end
183
+ rescue
184
+ STDERR.puts " #{$!.inspect}: falling back to #inspect for #{obj.class}\n #{$!.backtrace * "\n "}"
185
+ '<pre>' << wrap_lines(obj.inspect) << '</pre>'
186
+ end
187
+ end
188
+
189
+ def safe_format obj
190
+ safe_pp(obj)
191
+ end
192
+
193
+ def safe_pp obj
194
+ ::PP.pp(obj, '')
195
+ rescue
196
+ STDERR.puts " #{$!.inspect}: falling back to #inspect for #{obj.class}\n #{$!.backtrace * "\n "}"
197
+ obj.inspect
198
+ end
199
+
200
+ def format_as_terminal str
201
+ str &&= str.to_s.force_encoding('UTF-8')
202
+ if str.blank?
203
+ %Q{<span class="none">NONE</span>}
204
+ else
205
+ ansi2html(str)
206
+ end
207
+ end
208
+
209
+ def ansi2html ansi
210
+ Ansi2Html.new.convert(ansi, '')
211
+ end
212
+ end
213
+ end
214
+
@@ -0,0 +1,85 @@
1
+ require 'rack_console/mock_method'
2
+
3
+ module RackConsole
4
+ module MethodIntrospection
5
+ def methods_matching params
6
+ name_p = match_pred(params[:name], :to_sym)
7
+ kind_p = match_pred(params[:kind], :to_sym)
8
+ owner_p = match_pred(params[:owner])
9
+ file_p = match_pred(params[:file])
10
+
11
+ methods = [ ]
12
+ seen = { }
13
+ ObjectSpace.each_object(::Module) do | owner |
14
+ next unless (owner.name rescue nil)
15
+ next if owner_p && owner_p != owner.name
16
+ methods_for_module(owner, name_p, kind_p, file_p, seen, methods)
17
+ end
18
+ sort_methods! methods
19
+ methods
20
+ end
21
+
22
+ def match_pred value, m = nil
23
+ if value != nil && value != '*' && value != ''
24
+ value = value.send(m) if m
25
+ else
26
+ value = nil
27
+ end
28
+ value
29
+ end
30
+
31
+ def methods_for_module owner, name_p = nil, kind_p = nil, file_p = nil, seen = { }, to_methods = nil
32
+ methods = to_methods || [ ]
33
+ methods_for_module_by_kind([ :i, :instance_method_names, :instance_method ],
34
+ owner, name_p, kind_p, file_p, seen, methods)
35
+ methods_for_module_by_kind([ :c, :singleton_method_names, :singleton_method ],
36
+ owner, name_p, kind_p, file_p, seen, methods)
37
+ sort_methods! methods unless to_methods
38
+ methods
39
+ end
40
+
41
+ def methods_for_module_by_kind access, owner, name_p, kind_p, file_p, seen, methods
42
+ kind, method_names, method_getter = *access
43
+ unless kind_p && kind_p != kind
44
+ send(method_names, owner).each do | name |
45
+ next if name_p && name_p != (name = name.to_sym)
46
+ if meth = (owner.send(method_getter, name) rescue nil) and key = [ owner, kind, name ] and ! seen[key]
47
+ seen[key] = true
48
+ if file_p
49
+ f = meth.source_location and f = f.first
50
+ next if f != file_p
51
+ end
52
+ methods << MockMethod.new(meth, name, kind, owner)
53
+ end
54
+ end
55
+ end
56
+ methods
57
+ end
58
+
59
+ def sort_methods! methods
60
+ methods.sort_by!{|x| [ x.owner.to_s, x.kind, x.name ]}
61
+ end
62
+
63
+ def instance_method_names owner
64
+ ( owner.instance_methods(false) |
65
+ owner.private_instance_methods(false) |
66
+ owner.protected_instance_methods(false)
67
+ ).sort
68
+ end
69
+
70
+ def singleton_method_names owner
71
+ owner.singleton_methods(false)
72
+ end
73
+
74
+ def methods_within_file file
75
+ methods = methods_matching(file: file)
76
+ sort_methods_by_source_location! methods
77
+ end
78
+
79
+ def sort_methods_by_source_location! methods
80
+ methods.sort_by!{|x| x.source_location || DUMMY_SOURCE_LOCATION }
81
+ end
82
+ DUMMY_SOURCE_LOCATION = [ "".freeze, 0 ].freeze
83
+ end
84
+ end
85
+
@@ -1,5 +1,7 @@
1
1
  .rack_console {
2
- border: 1px solid black;
2
+ color: white;
3
+ background-color: black;
4
+ border: 1px solid white;
3
5
  padding: 1em;
4
6
  font-family: sans-serif;
5
7
  }
@@ -10,7 +12,7 @@
10
12
 
11
13
  .rack_console dt {
12
14
  font-size: 80%;
13
- color: #444;
15
+ color: #888;
14
16
  }
15
17
 
16
18
  .rack_console dd {
@@ -48,7 +50,7 @@
48
50
  }
49
51
 
50
52
  .rack_console a.hover_show:hover span {
51
- color: #000;
53
+ color: #aaa;
52
54
  display: initial;
53
55
  font-style: bold;
54
56
  }
@@ -62,6 +64,7 @@
62
64
  }
63
65
 
64
66
  .rack_console .result pre {
67
+ border: 1px solid white;
65
68
  font-family: monospace;
66
69
  font-size: 13px;
67
70
  background-color: #000;
@@ -89,24 +92,31 @@
89
92
  padding: 0.5em;
90
93
  }
91
94
 
95
+ .rack_console .constant .name {
96
+ color: #af4;
97
+ }
98
+ .rack_console .constant td {
99
+ padding-right: 1em;
100
+ }
101
+
92
102
  .rack_console span.module_name {
93
103
  font-family: monospace;
94
- color: #833;
104
+ color: #f66;
95
105
  }
96
106
 
97
107
  .rack_console span.method_name {
98
108
  font-family: monospace;
99
- color: #383;
109
+ color: #6f6;
100
110
  }
101
111
 
102
112
  .rack_console span.file_name {
103
113
  font-family: monospace;
104
- color: #338;
114
+ color: #aaf;
105
115
  }
106
116
 
107
117
  .rack_console span.literal {
108
118
  font-family: monospace;
109
- color: #388;
119
+ color: #6ff;
110
120
  }
111
121
 
112
122
  .rack_console .source_listing {
@@ -115,7 +125,7 @@
115
125
 
116
126
  .rack_console .ruby_core_doc {
117
127
  font-family: monospace;
118
- color: #883;
128
+ color: #ff6;
119
129
  }
120
130
 
121
131
  .rack_console tr.constant {
@@ -123,11 +133,30 @@
123
133
  }
124
134
 
125
135
  .rack_console .source_listing .unselected_line {
126
- color: #888;
136
+ color: #aba;
127
137
  }
128
138
 
129
139
  .rack_console .source_listing .selected_line {
130
- font-weight: bold;
140
+ font-weight: bolder;
141
+ color: #fff;
142
+ }
143
+
144
+ .rack_console .source_listing .block_begin {
145
+ font-weight: bolder;
146
+ color: #fff;
147
+ text-decoration: underline;
148
+ }
149
+
150
+ .rack_console .source_listing .block_body {
151
+ color: #eee;
152
+ }
153
+
154
+ .rack_console .file_name * {
155
+ color: #aaf;
156
+ }
157
+
158
+ .rack_console .file_name .methods {
159
+ color: #eee;
131
160
  }
132
161
 
133
162
  .rack_console .error dl.error {
@@ -146,16 +175,23 @@
146
175
  font-size: 75%;
147
176
  }
148
177
 
149
- .rack_console .methods td.class {
178
+ .rack_console tr.method td.class {
150
179
  text-align: right;
151
180
  }
152
181
 
153
- .rack_console .methods td.kind {
182
+ .rack_console tr.method td.kind * {
154
183
  font-family: monospace;
184
+ color: #cab;
155
185
  }
156
186
 
157
- .rack_console .methods td.source_location {
187
+ .rack_console tr.method td.source_location * {
158
188
  padding-left: 2em;
189
+ color: #aaf;
190
+ }
191
+
192
+ .rack_console tr.method td.source_location a.ruby_core_doc {
193
+ padding-left: 6em;
194
+ color: #77c;
159
195
  }
160
196
 
161
197
  .rack_console .ansi {
@@ -7,7 +7,7 @@
7
7
  %dt File:
8
8
  %dd.file_name
9
9
  =file_name_tag(h @source_file.file)
10
- %a{href: source_file_methods_href(@source_file.file)}
10
+ %a.methods{href: source_file_methods_href(@source_file.file)}
11
11
  (methods)
12
12
  %dt Source:
13
13
  %dd.source
@@ -37,10 +37,10 @@
37
37
  %table.constants
38
38
  - @constants.each do | name, value |
39
39
  %tr.constant
40
- %td
40
+ %td.name
41
41
  %tt=h name
42
- %td= format_module(value.class)
43
- %td= format_object(value, :inline)
42
+ %td.value_class= format_module(value.class)
43
+ %td.value= format_object(value, :inline)
44
44
  %dt
45
45
  Methods:
46
46
  %dd
@@ -5,14 +5,13 @@
5
5
  %link{rel: "stylesheet", href: url_root("/css/rack_console.css")}
6
6
  %link{rel: "stylesheet", href: url_root("/css/ansi.css")}
7
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.
8
+ .rack_console
9
+ .wrapper
10
+ .header
11
+ %h1 rack_console
12
+
13
+ .content
14
+ =yield
15
+
16
+ .footer
17
+ %hr
@@ -1,3 +1,3 @@
1
1
  module RackConsole
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.1"
3
3
  end
@@ -38,4 +38,5 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency "guard", "~> 2.12"
39
39
  spec.add_development_dependency "guard-rspec", "~> 4.5"
40
40
  spec.add_development_dependency "pry", "~> 0.10"
41
+ spec.add_development_dependency "simplecov", "~> 0.11"
41
42
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack_console
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.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-12-01 00:00:00.000000000 Z
11
+ date: 2016-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sinatra
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0.10'
153
+ - !ruby/object:Gem::Dependency
154
+ name: simplecov
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.11'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.11'
153
167
  description: A Rack App that provides a basic interactive Ruby console and introspection.
154
168
  email:
155
169
  - ks.github@kurtstephens.com
@@ -173,7 +187,10 @@ files:
173
187
  - lib/rack_console/ansi2html.rb
174
188
  - lib/rack_console/app.rb
175
189
  - lib/rack_console/app_helpers.rb
190
+ - lib/rack_console/configuration.rb
176
191
  - lib/rack_console/expr_helpers.rb
192
+ - lib/rack_console/formatting.rb
193
+ - lib/rack_console/method_introspection.rb
177
194
  - lib/rack_console/mock_method.rb
178
195
  - lib/rack_console/source_file.rb
179
196
  - lib/rack_console/template/css/ansi.css