rack_console 0.3.2 → 0.4.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.
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