rack_console 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.