rs 35
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.
- data/.gitignore +14 -0
- data/Rakefile +52 -0
- data/bin/rs +91 -0
- data/doc/LICENCE +29 -0
- data/doc/README.markdown +62 -0
- data/lib/rs/eval.rb +129 -0
- data/lib/rs/string.rb +39 -0
- data/lib/rs/ui.rb +114 -0
- data/setup.rb +1360 -0
- data/spec/evaluator_spec.rb +114 -0
- data/spec/spec_helper.rb +67 -0
- data/spec/ui_spec.rb +144 -0
- metadata +84 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
# Testing
|
4
|
+
require "spec_helper"
|
5
|
+
|
6
|
+
# Project
|
7
|
+
require "rs/eval"
|
8
|
+
|
9
|
+
|
10
|
+
# TODO: Dunno how much point there is to add stuff to spec
|
11
|
+
# here, but I suppose some coverage is useful. --rue
|
12
|
+
|
13
|
+
describe "Executing Ruby code with the evaluator" do
|
14
|
+
before :each do
|
15
|
+
@rs = RS::Evaluator.new
|
16
|
+
@topmeth = "rs_eval_toplevel_def_#{$$}"
|
17
|
+
end
|
18
|
+
|
19
|
+
after :each do
|
20
|
+
@rs.execute "Object.send :remove_method, :#{@topmeth} if Object.instance_methods.include?(:#{@topmeth})"
|
21
|
+
@rs.execute "Object.send :remove_const, :RSEvalSpecsClass if defined? RSEvalSpecsClass"
|
22
|
+
@rs.execute "Object.send :remove_const, :RSEvalSpecsModule if defined? RSEvalSpecsModule"
|
23
|
+
@rs.execute "Object.send :remove_const, :RSEvalSpecsExtModule if defined? RSEvalSpecsExtModule"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "has a top-level self object" do
|
27
|
+
@rs.execute("self").should be_kind_of(Object)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "provides #rs which gives access to an OpenStruct" do
|
31
|
+
@rs.execute("rs").should be_kind_of(OpenStruct)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "allows access to a #config OpenStruct through #rs" do
|
35
|
+
@rs.execute("rs.config").should be_kind_of(OpenStruct)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "allows top-level method definitions" do
|
39
|
+
@rs.execute("def #{@topmeth}; :gots; end").should == nil
|
40
|
+
@rs.execute(@topmeth).should == :gots
|
41
|
+
end
|
42
|
+
|
43
|
+
it "allows top-level class and module definitions" do
|
44
|
+
@rs.execute "class RSEvalSpecsClass; end"
|
45
|
+
@rs.execute "module RSEvalSpecsModule; end"
|
46
|
+
|
47
|
+
@rs.execute("RSEvalSpecsClass").should be_kind_of(Class)
|
48
|
+
@rs.execute("RSEvalSpecsModule").should be_kind_of(Module)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "has no wrapping nesting in top-level modules" do
|
52
|
+
@rs.execute("Module.nesting").should == []
|
53
|
+
@rs.execute("module RSEvalSpecsModule; Module.nesting; end").should == [RSEvalSpecsModule]
|
54
|
+
end
|
55
|
+
|
56
|
+
it "supports extending and including into self" do
|
57
|
+
@rs.execute "module RSEvalSpecsModule; def rsesm; end; end"
|
58
|
+
@rs.execute "module RSEvalSpecsExtModule; def rsesem; end; end"
|
59
|
+
|
60
|
+
@rs.execute "include RSEvalSpecsModule"
|
61
|
+
@rs.execute("Object.ancestors").include?(RSEvalSpecsModule).should == true
|
62
|
+
|
63
|
+
@rs.execute "extend RSEvalSpecsExtModule"
|
64
|
+
@rs.execute("class << self; ancestors; end").include?(RSEvalSpecsExtModule).should == true
|
65
|
+
end
|
66
|
+
|
67
|
+
it "gracefully returns error object if an error bubbles to top level" do
|
68
|
+
@rs.execute("raise SyntaxError").should be_kind_of(SyntaxError)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "supports normal error handling" do
|
72
|
+
@rs.execute("begin; raise SyntaxError; rescue SyntaxError; :yay; end").should == :yay
|
73
|
+
end
|
74
|
+
|
75
|
+
it "lets SystemExit bubble up" do
|
76
|
+
lambda { @rs.execute "exit 1" }.should raise_error(SystemExit)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns error object given top-level return" do
|
80
|
+
@rs.execute("return").should be_kind_of(Exception)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "returns error object given top-level break" do
|
84
|
+
@rs.execute("break").should be_kind_of(Exception)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "returns error object given top-level next" do
|
88
|
+
pending "Escapes on 1.8" if RUBY_VERSION =~ /1\.8/
|
89
|
+
@rs.execute("next").should be_kind_of(Exception)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "returns error object given top-level redo" do
|
93
|
+
pending "Escapes on 1.8" if RUBY_VERSION =~ /1\.8/
|
94
|
+
@rs.execute("redo").should be_kind_of(Exception)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
describe "Evaluator creation using RS.start" do
|
101
|
+
|
102
|
+
it "yields an Evaluator to the block given" do
|
103
|
+
RS.start {|rs| rs.should be_kind_of(RS::Evaluator) }
|
104
|
+
end
|
105
|
+
|
106
|
+
it "does not rescue errors raised in the block" do
|
107
|
+
lambda { RS.start {|rs| raise "hi" } }.should raise_error("hi")
|
108
|
+
end
|
109
|
+
|
110
|
+
it "gives access to the eval context's main object with #main" do
|
111
|
+
RS.start {|rs| rs.main.should be_eql(rs.execute "self") }
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require "tempfile"
|
4
|
+
|
5
|
+
|
6
|
+
class OutputMatcher
|
7
|
+
def initialize(expected, stream)
|
8
|
+
@expected = expected
|
9
|
+
@stream = stream
|
10
|
+
end
|
11
|
+
|
12
|
+
# TODO: cleanup unnecessary error stuffs
|
13
|
+
def matches?(block)
|
14
|
+
old_to = @stream.dup
|
15
|
+
|
16
|
+
# Obtain a filehandle to replace (works with Readline)
|
17
|
+
@stream.reopen File.open(File.join(Dir.tmpdir, "should_output_#{$$}"), "w+")
|
18
|
+
|
19
|
+
# Execute
|
20
|
+
block.call
|
21
|
+
|
22
|
+
# Restore
|
23
|
+
out = @stream.dup
|
24
|
+
@stream.reopen old_to
|
25
|
+
|
26
|
+
# Grab the data
|
27
|
+
out.rewind
|
28
|
+
@data = out.read
|
29
|
+
|
30
|
+
# Match up
|
31
|
+
case @expected
|
32
|
+
when Regexp
|
33
|
+
@data.should =~ @expected
|
34
|
+
else
|
35
|
+
@data.should == @expected
|
36
|
+
end
|
37
|
+
|
38
|
+
# Clean up
|
39
|
+
ensure
|
40
|
+
out.close if out and !out.closed?
|
41
|
+
|
42
|
+
# STDIO redirection will break else
|
43
|
+
begin
|
44
|
+
@stream.seek 0, IO::SEEK_END
|
45
|
+
rescue Errno::ESPIPE, Errno::EPIPE
|
46
|
+
# Ignore
|
47
|
+
end
|
48
|
+
|
49
|
+
FileUtils.rm out.path if out
|
50
|
+
end
|
51
|
+
|
52
|
+
def failure_message()
|
53
|
+
fail
|
54
|
+
# "Output to #{@stream.to_i} was:\n#{@data.inspect}\nshould have been:\n#{@expected.inspect}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def negative_failure_message()
|
58
|
+
fail
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Top-level matching
|
63
|
+
#
|
64
|
+
def output(expected, into_stream = $stdout)
|
65
|
+
OutputMatcher.new expected, into_stream
|
66
|
+
end
|
67
|
+
|
data/spec/ui_spec.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
# Testing
|
4
|
+
require "spec_helper"
|
5
|
+
|
6
|
+
# Project
|
7
|
+
require "rs/ui"
|
8
|
+
|
9
|
+
|
10
|
+
describe "UI creation" do
|
11
|
+
|
12
|
+
it "yields the UI instance to the given block" do
|
13
|
+
RS::UI.new {|ui| ui.should be_kind_of(RS::UI) }
|
14
|
+
end
|
15
|
+
|
16
|
+
it "provides some kind of a default prompt String" do
|
17
|
+
Readline.should_receive(:readline).with(an_instance_of(String), true).once.and_return nil
|
18
|
+
|
19
|
+
RS::UI.new {|ui| ui.run }
|
20
|
+
end
|
21
|
+
|
22
|
+
it "allows prompt to be assigned to" do
|
23
|
+
lambda {
|
24
|
+
RS::UI.new {|ui| ui.prompt = lambda { "hi>" } }
|
25
|
+
}.should_not raise_error
|
26
|
+
end
|
27
|
+
|
28
|
+
it "accepts a ^C handler in #on_SIGINT" do
|
29
|
+
lambda {
|
30
|
+
RS::UI.new {|ui| ui.on_SIGINT { :hi } }
|
31
|
+
}.should_not raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it "accepts a linewise input handler in #on_input" do
|
35
|
+
lambda {
|
36
|
+
RS::UI.new {|ui| ui.on_input {|i| i.reverse } }
|
37
|
+
}.should_not raise_error
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
describe "UI loop/processing" do
|
44
|
+
|
45
|
+
it "terminates when ^D, EOF, received." do
|
46
|
+
Readline.should_receive(:readline).once.and_return nil
|
47
|
+
|
48
|
+
RS::UI.new {|ui|
|
49
|
+
ui.run
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
it "calls the current prompt and uses its value going out for each loop" do
|
54
|
+
str = "rs_ui_spec_prompt #{$$}> "
|
55
|
+
|
56
|
+
prompt = Object.new
|
57
|
+
prompt.should_receive(:call).exactly(2).times.and_return { str }
|
58
|
+
|
59
|
+
inputs = ["hi\n", "ho\n", nil]
|
60
|
+
prompts = [//, str, str]
|
61
|
+
|
62
|
+
# TODO: Slightly iffy, improve.
|
63
|
+
Readline.should_receive(:readline).exactly(3).times {|output_prompt, _|
|
64
|
+
prompts.shift.should === output_prompt
|
65
|
+
inputs.shift
|
66
|
+
}
|
67
|
+
|
68
|
+
RS::UI.new {|ui|
|
69
|
+
ui.on_input {|input|
|
70
|
+
ui.prompt = prompt
|
71
|
+
}
|
72
|
+
|
73
|
+
ui.run
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
describe "UI event handling" do
|
81
|
+
|
82
|
+
it "calls block given to #on_SIGINT on ^C" do
|
83
|
+
inputs = [lambda { raise Interrupt }, lambda { nil }]
|
84
|
+
|
85
|
+
Readline.should_receive(:readline).exactly(2).times.and_return { inputs.shift.call }
|
86
|
+
|
87
|
+
called = false
|
88
|
+
|
89
|
+
RS::UI.new {|ui|
|
90
|
+
ui.on_SIGINT { called = true }
|
91
|
+
ui.run
|
92
|
+
}
|
93
|
+
|
94
|
+
called.should == true
|
95
|
+
end
|
96
|
+
|
97
|
+
it "continues processing after invoking #on_SIGINT" do
|
98
|
+
continued = false
|
99
|
+
|
100
|
+
inputs = [lambda { raise Interrupt }, lambda { continued = true; nil } ]
|
101
|
+
|
102
|
+
Readline.should_receive(:readline).exactly(2).times.and_return { inputs.shift.call }
|
103
|
+
|
104
|
+
RS::UI.new {|ui|
|
105
|
+
ui.on_SIGINT { :yay }
|
106
|
+
ui.run
|
107
|
+
}
|
108
|
+
|
109
|
+
continued.should == true
|
110
|
+
end
|
111
|
+
|
112
|
+
it "invokes #on_input block for each line of input until terminated by ^D" do
|
113
|
+
inputs = ["hi\n", "ha\n", "hoo bee\n", nil, "blee\n"]
|
114
|
+
expected = inputs.map {|i| i.chomp if i }
|
115
|
+
|
116
|
+
Readline.should_receive(:readline).exactly(4).times.and_return { inputs.shift }
|
117
|
+
|
118
|
+
RS::UI.new {|ui|
|
119
|
+
ui.on_input {|input|
|
120
|
+
input.should == expected.shift
|
121
|
+
}
|
122
|
+
|
123
|
+
ui.run
|
124
|
+
}
|
125
|
+
|
126
|
+
expected.should == [nil, "blee"]
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
describe "UI output" do
|
133
|
+
|
134
|
+
it "calls #to_s on its argument and writes it to output with a newline when using #puts" do
|
135
|
+
lambda {
|
136
|
+
RS::UI.new {|ui|
|
137
|
+
ui.puts "Hiya"
|
138
|
+
ui.puts "Really"
|
139
|
+
}
|
140
|
+
}.should output("Hiya\nReally\n")
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "35"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Eero Saynatkari
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-15 00:00:00 +02:00
|
13
|
+
default_executable: rs
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.3.0
|
24
|
+
version:
|
25
|
+
description: " rs is the result of my object-oriented shell musings, experiments and implementations. Its focus is above all on simplicity and the best possible implementation of modern shell usage and needs. "
|
26
|
+
email: rs@projects.kittensoft.org
|
27
|
+
executables:
|
28
|
+
- rs
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Rakefile
|
36
|
+
- bin/rs
|
37
|
+
- doc/LICENCE
|
38
|
+
- doc/README.markdown
|
39
|
+
- lib/rs/eval.rb
|
40
|
+
- lib/rs/string.rb
|
41
|
+
- lib/rs/ui.rb
|
42
|
+
- setup.rb
|
43
|
+
- spec/evaluator_spec.rb
|
44
|
+
- spec/spec_helper.rb
|
45
|
+
- spec/ui_spec.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://github.com/rue/rs
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message: |+
|
51
|
+
|
52
|
+
=======================================
|
53
|
+
|
54
|
+
/path/to/cwd rs> _
|
55
|
+
|
56
|
+
=======================================
|
57
|
+
|
58
|
+
rdoc_options:
|
59
|
+
- --charset=UTF-8
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: "0"
|
73
|
+
version:
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.3.5
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Object-oriented shell in full Ruby environment.
|
81
|
+
test_files:
|
82
|
+
- spec/evaluator_spec.rb
|
83
|
+
- spec/spec_helper.rb
|
84
|
+
- spec/ui_spec.rb
|