rib 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.gitmodules +3 -0
- data/.travis.yml +9 -0
- data/2011-02-28.md +203 -0
- data/CHANGES +86 -0
- data/CONTRIBUTORS +2 -0
- data/Gemfile +4 -0
- data/LICENSE +201 -0
- data/README +190 -0
- data/README.md +190 -0
- data/Rakefile +20 -0
- data/TODO +6 -0
- data/lib/rib.rb +42 -0
- data/lib/rib/all.rb +4 -0
- data/lib/rib/api.rb +105 -0
- data/lib/rib/core.rb +5 -0
- data/lib/rib/core/completion.rb +22 -0
- data/lib/rib/core/history_file.rb +38 -0
- data/lib/rib/core/readline.rb +19 -0
- data/lib/rib/core/underscore.rb +53 -0
- data/lib/rib/debug.rb +3 -0
- data/lib/rib/more.rb +12 -0
- data/lib/rib/more/color.rb +98 -0
- data/lib/rib/more/multiline.rb +77 -0
- data/lib/rib/more/multiline_history.rb +31 -0
- data/lib/rib/more/multiline_history_file.rb +37 -0
- data/lib/rib/more/squeeze_history.rb +37 -0
- data/lib/rib/more/strip_backtrace.rb +43 -0
- data/lib/rib/plugin.rb +56 -0
- data/lib/rib/runner.rb +106 -0
- data/lib/rib/shell.rb +43 -0
- data/lib/rib/test.rb +25 -0
- data/lib/rib/version.rb +4 -0
- data/lib/rib/zore.rb +3 -0
- data/lib/rib/zore/anchor.rb +69 -0
- data/lib/rib/zore/edit.rb +33 -0
- data/rib.gemspec +104 -0
- data/screenshot.png +0 -0
- data/task/.gitignore +1 -0
- data/task/gemgem.rb +182 -0
- data/test/core/test_completion.rb +18 -0
- data/test/core/test_history_file.rb +57 -0
- data/test/core/test_readline.rb +21 -0
- data/test/core/test_underscore.rb +41 -0
- data/test/more/test_color.rb +28 -0
- data/test/more/test_squeeze_history.rb +43 -0
- data/test/test_api.rb +20 -0
- data/test/test_plugin.rb +38 -0
- data/test/test_shell.rb +82 -0
- metadata +77 -13
data/lib/rib/all.rb
ADDED
data/lib/rib/api.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
|
2
|
+
module Rib; end
|
3
|
+
module Rib::API
|
4
|
+
def before_loop
|
5
|
+
read_history
|
6
|
+
self
|
7
|
+
end
|
8
|
+
|
9
|
+
def in_loop
|
10
|
+
input = catch(:rib_exit){ loop_once while true }
|
11
|
+
puts if input == nil
|
12
|
+
end
|
13
|
+
|
14
|
+
# Runs through one loop iteration: gets input, evals and prints result
|
15
|
+
def loop_once
|
16
|
+
self.error_raised = nil
|
17
|
+
input = get_input
|
18
|
+
throw(:rib_exit, input) if config[:exit].include?(input)
|
19
|
+
if input.strip == ''
|
20
|
+
eval_input(input)
|
21
|
+
else
|
22
|
+
print_result(eval_input(input))
|
23
|
+
end
|
24
|
+
rescue Interrupt
|
25
|
+
handle_interrupt
|
26
|
+
end
|
27
|
+
|
28
|
+
# Handles interrupt (Control-C) by printing a newline
|
29
|
+
def handle_interrupt() puts end
|
30
|
+
|
31
|
+
# Sets @result to result of evaling input and print unexpected errors
|
32
|
+
def eval_input(input)
|
33
|
+
loop_eval(input)
|
34
|
+
rescue Exception => e
|
35
|
+
self.error_raised = true
|
36
|
+
print_eval_error(e)
|
37
|
+
ensure
|
38
|
+
config[:line] += 1
|
39
|
+
end
|
40
|
+
|
41
|
+
# When extending this method, ensure your plugin disables readline:
|
42
|
+
# Readline.config[:readline] = false.
|
43
|
+
# @return [String, nil] Prints #prompt and returns input given by user
|
44
|
+
def get_input
|
45
|
+
print(prompt)
|
46
|
+
if input = $stdin.gets
|
47
|
+
input.chomp
|
48
|
+
else
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [String]
|
54
|
+
def prompt
|
55
|
+
config[:prompt]
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_history
|
59
|
+
end
|
60
|
+
|
61
|
+
def write_history
|
62
|
+
end
|
63
|
+
|
64
|
+
def history
|
65
|
+
@history ||= []
|
66
|
+
end
|
67
|
+
|
68
|
+
# Evals user input using @binding, @name and @line
|
69
|
+
def loop_eval(input)
|
70
|
+
config[:binding].eval(input, "(#{config[:name]})", config[:line])
|
71
|
+
end
|
72
|
+
|
73
|
+
# Prints error formatted by #format_error to STDERR. Could be extended to
|
74
|
+
# handle certain exceptions.
|
75
|
+
# @param [Exception]
|
76
|
+
def print_eval_error(err)
|
77
|
+
warn format_error(err)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Prints result using #format_result
|
81
|
+
def print_result(result)
|
82
|
+
puts(format_result(result)) unless error_raised
|
83
|
+
rescue StandardError, SyntaxError => e
|
84
|
+
warn "#{config[:name]}: Error while printing result:\n" \
|
85
|
+
"#{format_error(e)}"
|
86
|
+
end
|
87
|
+
|
88
|
+
# Formats errors raised by eval of user input
|
89
|
+
# @param [Exception]
|
90
|
+
# @return [String]
|
91
|
+
def format_error(e)
|
92
|
+
"#{e.class}: #{e.message}\n #{e.backtrace.join("\n ")}"
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [String] Formats result using result_prompt
|
96
|
+
def format_result(result)
|
97
|
+
config[:result_prompt] + result.inspect
|
98
|
+
end
|
99
|
+
|
100
|
+
# Called after shell finishes looping.
|
101
|
+
def after_loop
|
102
|
+
write_history
|
103
|
+
self
|
104
|
+
end
|
105
|
+
end
|
data/lib/rib/core.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
require 'rib'
|
3
|
+
require 'bond'
|
4
|
+
|
5
|
+
module Rib::Completion
|
6
|
+
include Rib::Plugin
|
7
|
+
Shell.use(self)
|
8
|
+
|
9
|
+
def before_loop
|
10
|
+
return super if Completion.disabled?
|
11
|
+
config[:completion] ||= {}
|
12
|
+
config[:completion][:eval_binding] ||= lambda{ config[:binding] }
|
13
|
+
(config[:completion][:gems] ||= []).concat(ripl_plugins)
|
14
|
+
Bond.start(config[:completion])
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def ripl_plugins
|
20
|
+
$LOADED_FEATURES.map{ |e| e[/ripl\/[^\/]+$/] }.compact
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
require 'rib'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Rib::HistoryFile
|
6
|
+
include Rib::Plugin
|
7
|
+
Shell.use(self)
|
8
|
+
|
9
|
+
def before_loop
|
10
|
+
return super if HistoryFile.disabled?
|
11
|
+
config[:history_file] ||= '~/.config/rib/history.rb'
|
12
|
+
config[:history_size] ||= 500
|
13
|
+
FileUtils.mkdir_p(File.dirname(history_file))
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_input
|
18
|
+
return super if HistoryFile.disabled?
|
19
|
+
(history << super).last
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_history
|
23
|
+
return super if HistoryFile.disabled?
|
24
|
+
File.exist?(history_file) && history.empty? &&
|
25
|
+
File.readlines(history_file).each{ |e| history << e.chomp }
|
26
|
+
end
|
27
|
+
|
28
|
+
def write_history
|
29
|
+
return super if HistoryFile.disabled?
|
30
|
+
File.open(history_file, 'w'){ |f|
|
31
|
+
f.puts(history.to_a.last(config[:history_size]).join("\n")) }
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
def history_file
|
36
|
+
@history_file ||= File.expand_path(config[:history_file])
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
require 'rib'
|
3
|
+
require 'readline'
|
4
|
+
|
5
|
+
module Rib::Readline
|
6
|
+
include Rib::Plugin
|
7
|
+
Shell.use(self)
|
8
|
+
|
9
|
+
def before_loop
|
10
|
+
return super if Readline.disabled?
|
11
|
+
@history = ::Readline::HISTORY
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_input
|
16
|
+
return super if Readline.disabled?
|
17
|
+
::Readline.readline(prompt, true)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
require 'rib'
|
3
|
+
|
4
|
+
module Rib::Underscore
|
5
|
+
include Rib::Plugin
|
6
|
+
Shell.use(self)
|
7
|
+
|
8
|
+
IVAR = {:_ => :@__rib_result__,
|
9
|
+
:__ => :@__rib_exception__}
|
10
|
+
|
11
|
+
def before_loop
|
12
|
+
return super if Underscore.disabled?
|
13
|
+
eliminate_warnings
|
14
|
+
inject_inspecter
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def loop_eval input
|
19
|
+
return super if Underscore.disabled?
|
20
|
+
bound_object.instance_variable_set(:@__rib_result__, super)
|
21
|
+
end
|
22
|
+
|
23
|
+
def print_eval_error e
|
24
|
+
return super if Underscore.disabled?
|
25
|
+
bound_object.instance_variable_set(:@__rib_exception__, e)
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def eliminate_warnings
|
31
|
+
IVAR.values.each{ |ivar| bound_object.instance_variable_set(ivar, nil) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def inject_inspecter
|
35
|
+
IVAR.each{ |k, v|
|
36
|
+
bound_singleton.send(:define_method, k){
|
37
|
+
instance_variable_get(v)
|
38
|
+
} unless bound_object.respond_to?(k) # only inject for innocences
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def bound_singleton
|
43
|
+
if respond_to?(:singleton_class)
|
44
|
+
bound_object.singleton_class
|
45
|
+
else
|
46
|
+
class << bound_object; self; end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def bound_object
|
51
|
+
config[:binding].eval('self')
|
52
|
+
end
|
53
|
+
end
|
data/lib/rib/debug.rb
ADDED
data/lib/rib/more.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
# upon session ends
|
3
|
+
require 'rib/more/squeeze_history'
|
4
|
+
require 'rib/more/multiline_history_file'
|
5
|
+
|
6
|
+
# upon formatting output
|
7
|
+
require 'rib/more/strip_backtrace'
|
8
|
+
require 'rib/more/color'
|
9
|
+
|
10
|
+
# upon input
|
11
|
+
require 'rib/more/multiline'
|
12
|
+
require 'rib/more/multiline_history'
|
@@ -0,0 +1,98 @@
|
|
1
|
+
|
2
|
+
require 'rib'
|
3
|
+
|
4
|
+
module Rib::Color
|
5
|
+
include Rib::Plugin
|
6
|
+
Shell.use(self)
|
7
|
+
|
8
|
+
def before_loop
|
9
|
+
config[:color] ||= {
|
10
|
+
Numeric => :red ,
|
11
|
+
String => :green ,
|
12
|
+
Symbol => :cyan ,
|
13
|
+
Array => :blue ,
|
14
|
+
Hash => :blue ,
|
15
|
+
NilClass => :magenta,
|
16
|
+
TrueClass => :magenta,
|
17
|
+
FalseClass => :magenta,
|
18
|
+
Exception => :magenta,
|
19
|
+
Object => :yellow
|
20
|
+
}
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def format_result result
|
25
|
+
return super if Color.disabled?
|
26
|
+
config[:result_prompt] + format_color(result)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def colors
|
31
|
+
config[:color]
|
32
|
+
end
|
33
|
+
|
34
|
+
def format_color result, display=result.inspect
|
35
|
+
case result
|
36
|
+
when String ; P.send(colors[String ]){ display }
|
37
|
+
when Numeric; P.send(colors[Numeric]){ display }
|
38
|
+
when Symbol ; P.send(colors[Symbol ]){ display }
|
39
|
+
|
40
|
+
when Array ; P.send(colors[Array ]){ '[' } +
|
41
|
+
result.map{ |e | format_color(e) }.
|
42
|
+
join(P.send(colors[Array ]){ ', ' }) +
|
43
|
+
P.send(colors[Array ]){ ']' }
|
44
|
+
|
45
|
+
when Hash ; P.send(colors[Hash ]){ '{' } +
|
46
|
+
result.map{ |k, v| format_color(k) +
|
47
|
+
P.send(colors[Hash ]){ '=>' } +
|
48
|
+
format_color(v) }.
|
49
|
+
join(P.send(colors[Hash ]){ ', ' }) +
|
50
|
+
P.send(colors[Hash ]){ '}' }
|
51
|
+
|
52
|
+
else ; if color = P.find_color(colors, result)
|
53
|
+
P.send(color){ display }
|
54
|
+
else
|
55
|
+
P.send(colors[Object ]){ display }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_error e, backtrace=e.backtrace
|
61
|
+
return super if Color.disabled?
|
62
|
+
[format_color(e, "#{e.class.to_s}: #{e.message}"),
|
63
|
+
backtrace.map{ |b|
|
64
|
+
path, rest = ::File.split(b)
|
65
|
+
name, msgs = rest.split(':', 2)
|
66
|
+
msg = msgs.sub(/(\d+):/){P.red{$1}+':'}.sub(/`.+?'/){P.green{$&}}
|
67
|
+
|
68
|
+
"#{path+'/'}#{P.yellow{name}}:#{msg}"
|
69
|
+
}]
|
70
|
+
end
|
71
|
+
|
72
|
+
module Imp
|
73
|
+
def find_color colors, value
|
74
|
+
(colors.sort{ |(k1, v1), (k2, v2)|
|
75
|
+
# Class <=> Class
|
76
|
+
if k1 < k2 then -1
|
77
|
+
elsif k1 > k2 then 1
|
78
|
+
else 0
|
79
|
+
end}.find{ |(klass, _)| value.kind_of?(klass) } || []).last
|
80
|
+
end
|
81
|
+
|
82
|
+
def color rgb
|
83
|
+
"\x1b[#{rgb}m" + (block_given? ? "#{yield}#{reset}" : '')
|
84
|
+
end
|
85
|
+
|
86
|
+
def black █ color(30, &block); end
|
87
|
+
def red █ color(31, &block); end
|
88
|
+
def green █ color(32, &block); end
|
89
|
+
def yellow █ color(33, &block); end
|
90
|
+
def blue █ color(34, &block); end
|
91
|
+
def magenta █ color(35, &block); end
|
92
|
+
def cyan █ color(36, &block); end
|
93
|
+
def white █ color(37, &block); end
|
94
|
+
def reset █ color('', &block); end
|
95
|
+
end
|
96
|
+
|
97
|
+
Plugin.extend(Imp)
|
98
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
|
2
|
+
require 'rib'
|
3
|
+
|
4
|
+
# from https://github.com/janlelis/ripl-multi_line
|
5
|
+
module Rib::Multiline
|
6
|
+
include Rib::Plugin
|
7
|
+
Shell.use(self)
|
8
|
+
|
9
|
+
# test those:
|
10
|
+
# ruby -e '"'
|
11
|
+
# ruby -e '{'
|
12
|
+
# ruby -e '['
|
13
|
+
# ruby -e '('
|
14
|
+
# ruby -e '/'
|
15
|
+
# ruby -e 'class C'
|
16
|
+
# ruby -e 'def f'
|
17
|
+
# ruby -e 'begin'
|
18
|
+
ERROR_REGEXP = Regexp.new(
|
19
|
+
[ # string or regexp
|
20
|
+
"unterminated \\w+ meets end of file",
|
21
|
+
# mri and rubinius
|
22
|
+
"syntax error, unexpected \\$end",
|
23
|
+
# rubinius
|
24
|
+
"expecting '.+'( or '.+')*",
|
25
|
+
# jruby
|
26
|
+
"syntax error, unexpected end-of-file",
|
27
|
+
].join('|'))
|
28
|
+
|
29
|
+
def prompt
|
30
|
+
return super if Multiline.disabled?
|
31
|
+
if multiline_buffer.empty?
|
32
|
+
super
|
33
|
+
else
|
34
|
+
"#{' '*(config[:prompt].size-2)}| "
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def loop_once
|
39
|
+
return super if Multiline.disabled?
|
40
|
+
catch(:multiline_cont) do
|
41
|
+
super
|
42
|
+
multiline_buffer.clear
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def loop_eval(input)
|
47
|
+
return super if Multiline.disabled?
|
48
|
+
multiline_buffer << input
|
49
|
+
super(multiline_buffer.join("\n"))
|
50
|
+
end
|
51
|
+
|
52
|
+
def print_eval_error(e)
|
53
|
+
return super if Multiline.disabled?
|
54
|
+
if e.is_a?(SyntaxError) && e.message =~ ERROR_REGEXP
|
55
|
+
throw :multiline_cont
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def handle_interrupt
|
62
|
+
return super if Multiline.disabled?
|
63
|
+
if multiline_buffer.empty?
|
64
|
+
super
|
65
|
+
else
|
66
|
+
line = multiline_buffer.pop
|
67
|
+
print "[removed this line: #{line}]"
|
68
|
+
super
|
69
|
+
throw :multiline_cont
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def multiline_buffer
|
75
|
+
@multiline_buffer ||= []
|
76
|
+
end
|
77
|
+
end
|