rs 35

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
+
@@ -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