Module: RackConsole::AppHelpers

Includes:
ExprHelpers
Defined in:
lib/rack_console/app_helpers.rb

Constant Summary collapse

DUMMY_SOURCE_LOCATION =
[ "".freeze, 0 ].freeze

Instance Method Summary collapse

Methods included from ExprHelpers

#expr_for_method, #expr_for_module, #expr_for_object, #expr_for_object!

Instance Method Details

#ansi2html(ansi) ⇒ Object



477
478
479
# File 'lib/rack_console/app_helpers.rb', line 477

def ansi2html ansi
  Ansi2Html.new.convert(ansi, '')
end

#capture_stdio!Object



117
118
119
120
121
122
123
124
125
126
# File 'lib/rack_console/app_helpers.rb', line 117

def capture_stdio!
  @captured_stdio = true
  _stdin, _stdout, _stderr = $stdin, $stdout, $stderr
  $stdin, $stdout, $stderr = @stdin, @stdout, @stderr
  begin
    yield
  ensure
    $stdin, $stdout, $stderr = _stdin, _stdout, _stderr
  end
end

#console!Object



62
63
64
# File 'lib/rack_console/app_helpers.rb', line 62

def console!
  haml :console, locals: locals, layout: layout
end

#const_get_safe(m, name) ⇒ Object



202
203
204
205
206
# File 'lib/rack_console/app_helpers.rb', line 202

def const_get_safe m, name
  m.const_get(name)
rescue Object
  "ERROR: #{$!.inspect}"
end

#css_dirObject



48
49
50
# File 'lib/rack_console/app_helpers.rb', line 48

def css_dir
  config[:css_dir]
end

#e(text) ⇒ Object



344
345
346
# File 'lib/rack_console/app_helpers.rb', line 344

def e(text)
  Rack::Utils.escape(text.to_s)
end

#evaluate_expr!Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/rack_console/app_helpers.rb', line 66

def evaluate_expr!
  return if @result_evaled
  result_capture! do
    @stdin  = StringIO.new('')
    @stdout = StringIO.new('')
    @stderr = StringIO.new('')
    @result_ok = false
    @expr = (params[:expr] || '').strip
    unless @expr.blank?
      @result_evaled = true
      Timeout.timeout(config[:eval_timeout] || 120) do
        capture_stdio! do
          expr_str = "begin; #{@expr} \n; end"
          @result = eval(expr_str)
          @result_ok = true
        end
      end
    end
  end
end

#evaluate_method!Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/rack_console/app_helpers.rb', line 98

def evaluate_method!
  evaluate_expr!
  @show_stdio = @show_result = false
  if @result_ok && @result.is_a?(Module)
    result_capture! do
      @module = @result
      @method_name = params[:name]
      @method_kind = params[:kind].to_s =~ /i/ ? :instance_method : :method
      @method = @module.send(@method_kind, @method_name) rescue nil
      unless @method
        @method = @module.send(:method, @method_name)
        @method_kind = :method
      end
      @result = @method
      expr_for_object! @method, @module, @method_kind
    end
  end
end

#evaluate_methods!Object



128
129
130
131
132
133
# File 'lib/rack_console/app_helpers.rb', line 128

def evaluate_methods!
  @methods = nil
  result_capture! do
    @methods = methods_matching(params)
  end
end

#evaluate_module!Object



87
88
89
90
91
92
93
94
95
96
# File 'lib/rack_console/app_helpers.rb', line 87

def evaluate_module!
  evaluate_expr!
  @show_stdio = @show_result = false
  if @result_ok && @result.is_a?(Module)
    result_capture! do
      @module = @result
      expr_for_object! @module
    end
  end
end

#file_line_to_href(name, lineno = nil) ⇒ Object



316
317
318
319
320
321
322
# File 'lib/rack_console/app_helpers.rb', line 316

def file_line_to_href name, lineno = nil
  link = name.sub(%r{^/}, '-')
  link = link.split('/').map{|s| e s}.join('/')
  link = url_root("/file/#{link}")
  link << ":#{lineno}\##{lineno - 2}" if lineno
  link
end

#file_name_tag(str) ⇒ Object



397
398
399
# File 'lib/rack_console/app_helpers.rb', line 397

def file_name_tag str
  %Q{<span class="file_name">#{str}</span>}
end

#find_template(views, name, engine, &block) ⇒ Object



40
41
42
43
44
45
46
# File 'lib/rack_console/app_helpers.rb', line 40

def find_template(views, name, engine, &block)
  views = config[:views] || views
  Array(views).each do |v|
    v = config[:views_default] if v == :default
    super(v, name, engine, &block)
  end
end

#format_as_terminal(str) ⇒ Object



468
469
470
471
472
473
474
475
# File 'lib/rack_console/app_helpers.rb', line 468

def format_as_terminal str
  str &&= str.to_s.force_encoding('UTF-8')
  if str.blank?
    %Q{<span class="none">NONE</span>}
  else
    ansi2html(str)
  end
end

#format_backtrace(line) ⇒ Object



413
414
415
416
417
418
419
420
421
422
# File 'lib/rack_console/app_helpers.rb', line 413

def format_backtrace line
  line = line.to_s
  html =
    if line =~ /^(.*):(\d+):(in .*)$/ && File.exist?($1)
      "#{format_source_location([$1, $2.to_i])}:#{h $3}"
    else
      file_name_tag(h line)
    end
  %Q{<span class="backtrace">#{html}</span>}
end

#format_method(m, kind, owner = nil) ⇒ Object



371
372
373
374
375
376
377
# File 'lib/rack_console/app_helpers.rb', line 371

def format_method m, kind, owner = nil
  owner ||= m.owner
  source_location = m.source_location
  source_location &&= source_location * ":"
  href = method_href(m, kind, owner)
  "<a href='#{href}' title='#{source_location}' class='method_name'>#{method_name_tag(h(m.name))}</a>"
end

#format_methods(obj) ⇒ Object



384
385
386
387
388
389
390
391
392
393
394
395
# File 'lib/rack_console/app_helpers.rb', line 384

def format_methods obj
  case obj
  when nil
    return nil
  when ::Method, ::UnboundMethod, MockMethod
    name = obj.name or return nil
  else
    name = obj.to_s
  end
  href = url_root("/methods/*/*/#{e name}")
  "<a href='#{href}' title='Other methods named #{h name.inspect}' class='method_name'>#{method_name_tag(h(name))}</a>"
end

#format_module(obj) ⇒ Object



356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/rack_console/app_helpers.rb', line 356

def format_module obj
  return module_name_tag(h(obj.inspect)) unless obj && obj.name
  path = obj.name.to_s.split('::')
  result = ''
  name = ''; pre = ''
  path.each do | n |
    name << n
    href = url_root("/module/#{name}")
    result << "<a href='#{href}' class='module_name'>#{module_name_tag("#{pre}#{h n}")}</a>"
    name << '::'
    pre = '::'
  end
  module_name_tag(result)
end

#format_object(obj, inline = false) ⇒ Object



185
186
187
188
189
190
191
192
# File 'lib/rack_console/app_helpers.rb', line 185

def format_object obj, inline = false
  case obj
  when Module
    format_module obj
  else
    format_other obj, inline
  end
end

#format_other(obj, inline = false) ⇒ Object



194
195
196
197
198
199
200
# File 'lib/rack_console/app_helpers.rb', line 194

def format_other obj, inline = false
  if inline
    literal_tag(h(limit_string(safe_format(obj), 80)))
  else
    safe_format_structured(obj)
  end
end

#format_source_location(source_location, meth = nil, kind = nil, owner = nil) ⇒ Object



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/rack_console/app_helpers.rb', line 208

def format_source_location source_location, meth = nil, kind = nil, owner = nil
  file, line = source_location
  if file
    "<a href='#{file_line_to_href file, line}' class='file_name'>#{file_name_tag("#{file}:#{line}")}</a>"
  else
    if meth
      # Assume meth is Ruby Core and link to rdocs on ruby-doc.org.
      name = meth.name
      owner ||= meth.owner
      owner_name = (owner.name || '').gsub('::', '/')
      kind ||= (owner.instance_method(name) rescue nil) ? :i : :c
      a_name = name.to_s.gsub(/([^a-z0-9_])/i){|m| "-%X" % [ m.ord ]}
      a_name.sub!(/^-/, '')
      a_name = "method-#{kind}-#{a_name}"
      ruby_core_link = "http://www.ruby-doc.org/core-#{RUBY_VERSION}/#{owner_name}.html\##{a_name}"
      "<a href='#{ruby_core_link}' class='ruby_core_doc'>#{h ruby_core_link}</a>"
    else
      "NONE"
    end
  end
end

#h(text) ⇒ Object



340
341
342
# File 'lib/rack_console/app_helpers.rb', line 340

def h(text)
  Rack::Utils.escape_html(text.to_s)
end

#has_console_access?Boolean



13
14
15
16
17
18
19
20
21
22
# File 'lib/rack_console/app_helpers.rb', line 13

def has_console_access?
  case a = config[:authorized?]
  when true, false
    a
  when Proc
    a.call
  else
    true
  end
end

#has_file_access?(file) ⇒ Boolean



24
25
26
# File 'lib/rack_console/app_helpers.rb', line 24

def has_file_access? file
  ! ! (file != '(eval)' && $".include?(file))
end

#href_to_file_line(path) ⇒ Object



324
325
326
327
328
329
# File 'lib/rack_console/app_helpers.rb', line 324

def href_to_file_line path
  path.to_s =~ /^([^:]+)(:([^:]+))?/
  file, line = $1, $3
  file.sub!(/^-/, '/')
  [ file, line && line.to_i ]
end

#instance_method_names(owner) ⇒ Object



295
296
297
298
299
300
# File 'lib/rack_console/app_helpers.rb', line 295

def instance_method_names owner
  ( owner.instance_methods(false) |
    owner.private_instance_methods(false) |
    owner.protected_instance_methods(false)
    ).sort
end

#layoutObject



36
37
38
# File 'lib/rack_console/app_helpers.rb', line 36

def layout
  config[:layout] || :layout
end

#limit_string(text, len) ⇒ Object



348
349
350
351
352
353
354
# File 'lib/rack_console/app_helpers.rb', line 348

def limit_string(text, len)
  text = text.to_s
  if text.size > len
    text = text[0 .. len] + ' ...'
  end
  text
end

#literal_tag(str) ⇒ Object



409
410
411
# File 'lib/rack_console/app_helpers.rb', line 409

def literal_tag str
  %Q{<span class="literal">#{str}</span>}
end

#localsObject



32
33
34
# File 'lib/rack_console/app_helpers.rb', line 32

def locals
  @locals ||= { }
end

#match_pred(value, m = nil) ⇒ Object



247
248
249
250
251
252
253
254
# File 'lib/rack_console/app_helpers.rb', line 247

def match_pred value, m = nil
  if value != nil && value != '*' && value != ''
    value = value.send(m) if m
  else
    value = nil
  end
  value
end

#method_href(m, kind, owner = nil) ⇒ Object



379
380
381
382
# File 'lib/rack_console/app_helpers.rb', line 379

def method_href m, kind, owner = nil
  owner ||= m.owner
  href = url_root("/method/#{owner.name}/#{e kind.to_s}/#{e m.name}")
end

#method_name_tag(str) ⇒ Object



405
406
407
# File 'lib/rack_console/app_helpers.rb', line 405

def method_name_tag str
  %Q{<span class="method_name">#{str}</span>}
end

#methods_for_module(owner, name_p = nil, kind_p = nil, file_p = nil, seen = { }, to_methods = nil) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/rack_console/app_helpers.rb', line 256

def methods_for_module owner, name_p = nil, kind_p = nil, file_p = nil, seen = { }, to_methods = nil
  methods = to_methods || [ ]
  kind = :i
  unless kind_p && kind_p != kind
    instance_method_names(owner).each do | name |
      next if name_p && name_p != (name = name.to_sym)
      if meth = (owner.instance_method(name) rescue nil) and key = [ owner, kind, name ] and ! seen[key]
        seen[key] = true
        if file_p
          f = meth.source_location and f = f.first
          next if f != file_p
        end
        methods << MockMethod.new(meth, name, kind, owner)
      end
    end
  end

  kind = :c
  unless kind_p && kind_p != kind
    singleton_method_names(owner).each do | name |
      next if name_p && name_p != (name = name.to_sym)
      if meth = (owner.singleton_method(name) rescue nil) and key = [ owner, kind, name ] and ! seen[key]
        seen[key] = true
        if file_p
          f = meth.source_location and f = f.first
          next if f != file_p
        end
        methods << MockMethod.new(meth, name, kind, owner)
      end
    end
  end
  sort_methods! methods unless to_methods
  methods
end

#methods_matching(params) ⇒ Object



230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/rack_console/app_helpers.rb', line 230

def methods_matching params
  name_p  = match_pred(params[:name], :to_sym)
  kind_p  = match_pred(params[:kind], :to_sym)
  owner_p = match_pred(params[:owner])
  file_p  = match_pred(params[:file])

  methods = [ ]
  seen = { }
  ObjectSpace.each_object(::Module) do | owner |
    next unless (owner.name rescue nil)
    next if owner_p && owner_p != owner.name
    methods_for_module(owner, name_p, kind_p, file_p, seen, methods)
  end
  sort_methods! methods
  methods
end

#methods_within_file(file) ⇒ Object



306
307
308
309
# File 'lib/rack_console/app_helpers.rb', line 306

def methods_within_file file
  methods = methods_matching(file: file)
  sort_methods_by_source_location! methods
end

#module_name_tag(str) ⇒ Object



401
402
403
# File 'lib/rack_console/app_helpers.rb', line 401

def module_name_tag str
  %Q{<span class="module_name">#{str}</span>}
end

#prepare_file!Object



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/rack_console/app_helpers.rb', line 135

def prepare_file!
  path = params[:splat][0]
  file, line = href_to_file_line(path)
  result_capture! do
    unless has_file_access? file
      content_type 'text/plain'
      return "NOT A LOADABLE FILE"
    end
    @source_file = SourceFile.new([ file, line ]).load!
  end
end

#result_capture!Object



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/rack_console/app_helpers.rb', line 147

def result_capture!
  @result_ok = false
  result = yield
  @result_ok = true
  result
rescue
  @error = $!
  @error_description = @error.inspect
ensure
  @result_extended = @result.singleton_class.included_modules rescue nil
  @result_class = @result.class.name
  if @is_module = (::Module === @result)
    @module = @result
    @ancestors = @module.ancestors.drop(1)
    if @is_class = (::Class === @module)
      @superclass = @module.superclass
      @subclasses = @module.subclasses.sort_by{|c| c.name || ''}
    end
    @constants = @module.constants(false).sort.map{|n| [ n, const_get_safe(@module, n) ]}
    @methods = methods_for_module(@module)
  end
  if @is_method = (::Method === @result || ::UnboundMethod === @result || MockMethod === @result)
    @method = @result
    @method_source_location = @method.source_location
    @method_source = @method_source_location && SourceFile.new(@method_source_location).load!.narrow_to_block!
  end
end

#safe_format(obj) ⇒ Object



457
458
459
# File 'lib/rack_console/app_helpers.rb', line 457

def safe_format obj
  safe_pp(obj)
end

#safe_format_structured(obj) ⇒ Object



443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/rack_console/app_helpers.rb', line 443

def safe_format_structured obj
  begin
    if config[:awesome_print] && defined?(::AwesomePrint)
      ansi = obj.ai(indent: 2, html: false, index: false)
      ansi2html(ansi)
    else
      '<pre>' << wrap_lines(safe_pp(obj)) << '</pre>'
    end
  rescue
    STDERR.puts "  #{$!.inspect}: falling back to #inspect for #{obj.class}\n  #{$!.backtrace * "\n  "}"
    '<pre>' << wrap_lines(obj.inspect) << '</pre>'
  end
end

#safe_pp(obj) ⇒ Object



461
462
463
464
465
466
# File 'lib/rack_console/app_helpers.rb', line 461

def safe_pp obj
  ::PP.pp(obj, '')
rescue
  STDERR.puts "  #{$!.inspect}: falling back to #inspect for #{obj.class}\n  #{$!.backtrace * "\n  "}"
  obj.inspect
end

#server_infoObject



175
176
177
178
179
180
181
182
183
# File 'lib/rack_console/app_helpers.rb', line 175

def server_info
  thr = Thread.current
  (config[:server_info] || { }).merge(
    host: Socket.gethostname,
    pid: Process.pid,
    ppid: Process.ppid,
    thread: thr[:name] || thr.object_id,
  )
end

#singleton_method_names(owner) ⇒ Object



302
303
304
# File 'lib/rack_console/app_helpers.rb', line 302

def singleton_method_names owner
  owner.singleton_methods(false)
end

#sort_methods!(methods) ⇒ Object



291
292
293
# File 'lib/rack_console/app_helpers.rb', line 291

def sort_methods! methods
  methods.sort_by!{|x| [ x.owner.to_s, x.kind, x.name ]}
end

#sort_methods_by_source_location!(methods) ⇒ Object



311
312
313
# File 'lib/rack_console/app_helpers.rb', line 311

def sort_methods_by_source_location! methods
  methods.sort_by!{|x| x.source_location || DUMMY_SOURCE_LOCATION }
end

#source_file(source_location) ⇒ Object



336
337
338
# File 'lib/rack_console/app_helpers.rb', line 336

def source_file source_location
  source_location && SourceFile.new(source_location).load!
end

#source_file_methods_href(file) ⇒ Object



331
332
333
334
# File 'lib/rack_console/app_helpers.rb', line 331

def source_file_methods_href file
  link = file.sub(%r{^/}, '-')
  link = url_root("/methods/file/#{link}")
end

#url_root(url) ⇒ Object



28
29
30
# File 'lib/rack_console/app_helpers.rb', line 28

def url_root url
  "#{config[:url_root_prefix]}#{url}"
end

#with_accessObject



54
55
56
57
58
59
60
# File 'lib/rack_console/app_helpers.rb', line 54

def with_access
  if has_console_access?
    yield
  else
    raise Error, "not authorized"
  end
end

#wrap_line(str, width = nil) ⇒ Object



430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/rack_console/app_helpers.rb', line 430

def wrap_line str, width = nil
  width ||= config[:wrap_width] || 80
  str = str.to_s
  out = ''
  pos = 0
  while pos < str.size
    out << h(str[pos, width])
    pos += width
    out << "&nbsp;\u21B5\n" if pos < str.size
  end
  out
end

#wrap_lines(str, width = nil) ⇒ Object



424
425
426
427
428
# File 'lib/rack_console/app_helpers.rb', line 424

def wrap_lines str, width = nil
  str.to_s.split("\n").map do | line |
    wrap_line line, width
  end * "\n"
end