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
data/.gitignore
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
begin
|
2
|
+
require "rubygems"
|
3
|
+
require "jeweler"
|
4
|
+
|
5
|
+
namespace :jeweler do
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rs"
|
8
|
+
|
9
|
+
gem.version = `git log --pretty=oneline | wc -l`.chomp
|
10
|
+
|
11
|
+
|
12
|
+
gem.summary = "Object-oriented shell in full Ruby environment."
|
13
|
+
gem.description = <<-END.gsub /\s+/, " "
|
14
|
+
|
15
|
+
rs is the result of my object-oriented shell musings,
|
16
|
+
experiments and implementations. Its focus is above
|
17
|
+
all on simplicity and the best possible implementation
|
18
|
+
of modern shell usage and needs.
|
19
|
+
|
20
|
+
END
|
21
|
+
|
22
|
+
gem.email = "rs@projects.kittensoft.org"
|
23
|
+
gem.homepage = "http://github.com/rue/rs"
|
24
|
+
gem.authors = ["Eero Saynatkari"]
|
25
|
+
|
26
|
+
|
27
|
+
gem.add_development_dependency "rspec", ">= 1.3.0"
|
28
|
+
|
29
|
+
gem.post_install_message = <<-END
|
30
|
+
|
31
|
+
=======================================
|
32
|
+
|
33
|
+
/path/to/cwd rs> _
|
34
|
+
|
35
|
+
=======================================
|
36
|
+
|
37
|
+
END
|
38
|
+
end
|
39
|
+
|
40
|
+
Jeweler::GemcutterTasks.new
|
41
|
+
end
|
42
|
+
|
43
|
+
rescue LoadError
|
44
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# Main tasks
|
49
|
+
|
50
|
+
desc "Generate Gem and push it."
|
51
|
+
task :release_gem => %w[jeweler:gemcutter:release]
|
52
|
+
|
data/bin/rs
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# The 'executable' rs program.
|
4
|
+
#
|
5
|
+
# == Copyright
|
6
|
+
# Copyright (c) 2005-2010 Eero Saynatkari, all rights reserved.
|
7
|
+
#
|
8
|
+
# == Licence
|
9
|
+
# Redistribution and use in source and binary forms, with or without
|
10
|
+
# modification, are permitted provided that the following conditions
|
11
|
+
# are met:
|
12
|
+
#
|
13
|
+
# - Redistributions of source code must retain the above copyright
|
14
|
+
# notice, this list of conditions, the following disclaimer and
|
15
|
+
# attribution to the original authors.
|
16
|
+
#
|
17
|
+
# - Redistributions in binary form must reproduce the above copyright
|
18
|
+
# notice, this list of conditions, the following disclaimer and
|
19
|
+
# attribution to the original authors in the documentation and/or
|
20
|
+
# other materials provided with the distribution.
|
21
|
+
#
|
22
|
+
# - The names of the authors may not be used to endorse or promote
|
23
|
+
# products derived from this software without specific prior
|
24
|
+
# written permission.
|
25
|
+
#
|
26
|
+
# == Disclaimer
|
27
|
+
# This software is provided "as is" and without any express or
|
28
|
+
# implied warranties, including, without limitation, the implied
|
29
|
+
# warranties of merchantability and fitness for a particular purpose.
|
30
|
+
# Authors are not responsible for any damages, direct or indirect.
|
31
|
+
|
32
|
+
|
33
|
+
# We might need the files in development
|
34
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
35
|
+
|
36
|
+
|
37
|
+
# System
|
38
|
+
|
39
|
+
# Project
|
40
|
+
require "rs/eval"
|
41
|
+
require "rs/ui"
|
42
|
+
|
43
|
+
|
44
|
+
# TODO: Yeah...ugh. --rue
|
45
|
+
#
|
46
|
+
RS.start {|rs|
|
47
|
+
|
48
|
+
# Minor configulabulous
|
49
|
+
config = rs.main.send(:rs).config
|
50
|
+
|
51
|
+
config.default_prompt = lambda { "#{Dir.pwd} rs> " }
|
52
|
+
config.continuation_prompt = lambda { "#{Dir.pwd} ..> " }
|
53
|
+
|
54
|
+
|
55
|
+
RS::UI.new {|ui|
|
56
|
+
|
57
|
+
ui.on_SIGINT {
|
58
|
+
@earlier_incomplete = nil
|
59
|
+
ui.puts ""
|
60
|
+
ui.prompt = config.default_prompt
|
61
|
+
}
|
62
|
+
|
63
|
+
ui.on_input {|line|
|
64
|
+
next if line.empty?
|
65
|
+
|
66
|
+
if @earlier_incomplete
|
67
|
+
line = @earlier_incomplete + line
|
68
|
+
@earlier_incomplete = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
output = rs.execute line
|
73
|
+
|
74
|
+
ui.puts "=> #{output.inspect}"
|
75
|
+
ui.prompt = config.default_prompt
|
76
|
+
|
77
|
+
rescue RS::IncompleteExpression
|
78
|
+
@earlier_incomplete = line + "\n" # Only allow where newlines appropriate.
|
79
|
+
ui.prompt = config.continuation_prompt
|
80
|
+
|
81
|
+
rescue LocalJumpError => e # next and redo in 1.8
|
82
|
+
output = e
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
ui.prompt = config.default_prompt
|
87
|
+
ui.run
|
88
|
+
}
|
89
|
+
|
90
|
+
}
|
91
|
+
|
data/doc/LICENCE
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
= LICENCE
|
2
|
+
|
3
|
+
== Copyright
|
4
|
+
Copyright (c) 2005-2010 Eero Saynatkari, all rights reserved.
|
5
|
+
|
6
|
+
== Licence
|
7
|
+
Redistribution and use in source and binary forms, with or without
|
8
|
+
modification, are permitted provided that the following conditions
|
9
|
+
are met:
|
10
|
+
|
11
|
+
- Redistributions of source code must retain the above copyright
|
12
|
+
notice, this list of conditions, the following disclaimer and
|
13
|
+
attribution to the original authors.
|
14
|
+
|
15
|
+
- Redistributions in binary form must reproduce the above copyright
|
16
|
+
notice, this list of conditions, the following disclaimer and
|
17
|
+
attribution to the original authors in the documentation and/or
|
18
|
+
other materials provided with the distribution.
|
19
|
+
|
20
|
+
- The names of the authors may not be used to endorse or promote
|
21
|
+
products derived from this software without specific prior
|
22
|
+
written permission.
|
23
|
+
|
24
|
+
== Disclaimer
|
25
|
+
This software is provided "as is" and without any express or
|
26
|
+
implied warranties, including, without limitation, the implied
|
27
|
+
warranties of merchantability and fitness for a particular purpose.
|
28
|
+
Authors are not responsible for any damages, direct or indirect.
|
29
|
+
|
data/doc/README.markdown
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
rs
|
2
|
+
====
|
3
|
+
|
4
|
+
rs is the result of my object-oriented shell musings, experiments and
|
5
|
+
implementations. Its focus is above all on simplicity and the best
|
6
|
+
possible implementation of modern shell usage and needs.
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
Features
|
11
|
+
==========
|
12
|
+
|
13
|
+
Currently extremely rudimentary and exploratory:
|
14
|
+
|
15
|
+
- Executes normal Ruby code, unrestricted.
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
Requirements
|
20
|
+
==============
|
21
|
+
|
22
|
+
- Ruby
|
23
|
+
- 1.8.7, 1.9.1, 1.9.2
|
24
|
+
|
25
|
+
- For development
|
26
|
+
- RSpec (>= 1.3.0)
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
Where?
|
31
|
+
========
|
32
|
+
|
33
|
+
$ gem install rs
|
34
|
+
|
35
|
+
Source is found on [Github](http://github.com/rue/rs).
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
Who Do I Complain To?
|
40
|
+
=======================
|
41
|
+
|
42
|
+
* rs MEOW projects _purr_ kittensoft _rawr_ org.
|
43
|
+
* IRC channels #rs and ##rs on Freenode ('rue', if not the only person.)
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
Legal
|
48
|
+
=======
|
49
|
+
|
50
|
+
Copyright (c) 2005-2010 Eero Saynatkari. See doc/LICENCE.
|
51
|
+
|
52
|
+
|
53
|
+
|
54
|
+
Acknowledgements
|
55
|
+
==================
|
56
|
+
|
57
|
+
The original spark was provided by Reyn Vlietstra's ruSH back in 2005.
|
58
|
+
It was an excellent implementation of a novel concept, and I thoroughly
|
59
|
+
enjoyed working on it. The first incarnation of rs was a complete rewrite
|
60
|
+
effort of ruSH, and now I am starting from scratch again. Thanks to Reyn
|
61
|
+
and all the other collaborators on ruSH and the earlier rs.
|
62
|
+
|
data/lib/rs/eval.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# == Copyright
|
2
|
+
# Copyright (c) 2005-2010 Eero Saynatkari, all rights reserved.
|
3
|
+
#
|
4
|
+
# == Licence
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions
|
7
|
+
# are met:
|
8
|
+
#
|
9
|
+
# - Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions, the following disclaimer and
|
11
|
+
# attribution to the original authors.
|
12
|
+
#
|
13
|
+
# - Redistributions in binary form must reproduce the above copyright
|
14
|
+
# notice, this list of conditions, the following disclaimer and
|
15
|
+
# attribution to the original authors in the documentation and/or
|
16
|
+
# other materials provided with the distribution.
|
17
|
+
#
|
18
|
+
# - The names of the authors may not be used to endorse or promote
|
19
|
+
# products derived from this software without specific prior
|
20
|
+
# written permission.
|
21
|
+
#
|
22
|
+
# == Disclaimer
|
23
|
+
# This software is provided "as is" and without any express or
|
24
|
+
# implied warranties, including, without limitation, the implied
|
25
|
+
# warranties of merchantability and fitness for a particular purpose.
|
26
|
+
# Authors are not responsible for any damages, direct or indirect.
|
27
|
+
|
28
|
+
# Libs
|
29
|
+
require "ostruct"
|
30
|
+
|
31
|
+
|
32
|
+
module RS
|
33
|
+
|
34
|
+
# Creates and yields a new Evaluator.
|
35
|
+
#
|
36
|
+
# The block given should implement whatever logic is
|
37
|
+
# desired. Will clean up as necessary.
|
38
|
+
#
|
39
|
+
# TODO: This needs much improvement, probably. --rue
|
40
|
+
#
|
41
|
+
def self.start(&block)
|
42
|
+
rs = Evaluator.new
|
43
|
+
|
44
|
+
begin
|
45
|
+
rs.send :run, &block
|
46
|
+
|
47
|
+
rescue SystemExit => e
|
48
|
+
# No need to print errors etc., but set return code.
|
49
|
+
exit! e.status
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
#
|
55
|
+
# Error raised trying to execute an incomplete expression.
|
56
|
+
#
|
57
|
+
class IncompleteExpression < SyntaxError; end
|
58
|
+
|
59
|
+
|
60
|
+
# Runtime environment for executing user input as Ruby.
|
61
|
+
#
|
62
|
+
class Evaluator
|
63
|
+
|
64
|
+
# Create a new execution environment.
|
65
|
+
#
|
66
|
+
# You should use RS.start instead.
|
67
|
+
#
|
68
|
+
# TODO: Generate unique bindings?
|
69
|
+
#
|
70
|
+
def initialize()
|
71
|
+
@binding = eval "lambda { binding }.call", TOPLEVEL_BINDING
|
72
|
+
|
73
|
+
stash = OpenStruct.new :evaluator => self,
|
74
|
+
:config => OpenStruct.new
|
75
|
+
|
76
|
+
@main = execute "self"
|
77
|
+
@main.instance_variable_set "@rs", stash
|
78
|
+
|
79
|
+
# Work around MRI changing description.
|
80
|
+
def @main.inspect(); :rs_main; end
|
81
|
+
|
82
|
+
execute "def rs(); @rs; end"
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# Allow accessing main from the outside.
|
87
|
+
#
|
88
|
+
attr_reader :main
|
89
|
+
|
90
|
+
|
91
|
+
# Execute a presumably valid String of Ruby code.
|
92
|
+
#
|
93
|
+
# Trying to execute an incomplete Ruby expression
|
94
|
+
# raises IncompleteExpression.
|
95
|
+
#
|
96
|
+
# Errors are caught etc. (except top-level next and redo
|
97
|
+
# LocalJumpErrors, those need to be caught outside this
|
98
|
+
# scope), and returned as objects.
|
99
|
+
#
|
100
|
+
def execute(expression, file = "<no file>", line = "<no line>")
|
101
|
+
eval expression, @binding
|
102
|
+
|
103
|
+
rescue SyntaxError => e
|
104
|
+
case e.message
|
105
|
+
when /(parse|syntax) error.*?\$end/i, /unterminated/i # Should catch most
|
106
|
+
raise IncompleteExpression
|
107
|
+
else
|
108
|
+
e
|
109
|
+
end
|
110
|
+
rescue SystemExit
|
111
|
+
raise
|
112
|
+
rescue Exception => e
|
113
|
+
e
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
# Yields this instance to block.
|
118
|
+
#
|
119
|
+
# Use RS.start.
|
120
|
+
#
|
121
|
+
def run()
|
122
|
+
yield self
|
123
|
+
end
|
124
|
+
private :run
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
data/lib/rs/string.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# == Authors
|
2
|
+
# Please see doc/AUTHORS.
|
3
|
+
#
|
4
|
+
# == Copyright
|
5
|
+
# Copyright (c) 2005-2010 the Authors, all rights reserved.
|
6
|
+
#
|
7
|
+
# == Licence
|
8
|
+
# Redistribution and use in source and binary forms, with or without
|
9
|
+
# modification, are permitted provided that the following conditions
|
10
|
+
# are met:
|
11
|
+
#
|
12
|
+
# - Redistributions of source code must retain the above copyright
|
13
|
+
# notice, this list of conditions, the following disclaimer and
|
14
|
+
# attribution to the original authors.
|
15
|
+
#
|
16
|
+
# - Redistributions in binary form must reproduce the above copyright
|
17
|
+
# notice, this list of conditions, the following disclaimer and
|
18
|
+
# attribution to the original authors in the documentation and/or
|
19
|
+
# other materials provided with the distribution.
|
20
|
+
#
|
21
|
+
# - The names of the authors may not be used to endorse or promote
|
22
|
+
# products derived from this software without specific prior
|
23
|
+
# written permission.
|
24
|
+
#
|
25
|
+
# == Disclaimer
|
26
|
+
# This software is provided "as is" and without any express or
|
27
|
+
# implied warranties, including, without limitation, the implied
|
28
|
+
# warranties of merchantability and fitness for a particular purpose.
|
29
|
+
# Authors are not responsible for any damages, direct or indirect.
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
# Extensions to String needed for rs.
|
34
|
+
#
|
35
|
+
class String
|
36
|
+
|
37
|
+
# Nothing yet, but will need this later anyway.. --rue
|
38
|
+
|
39
|
+
end
|
data/lib/rs/ui.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# == Copyright
|
2
|
+
# Copyright (c) 2005-2010 Eero Saynatkari, all rights reserved.
|
3
|
+
#
|
4
|
+
# == Licence
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions
|
7
|
+
# are met:
|
8
|
+
#
|
9
|
+
# - Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions, the following disclaimer and
|
11
|
+
# attribution to the original authors.
|
12
|
+
#
|
13
|
+
# - Redistributions in binary form must reproduce the above copyright
|
14
|
+
# notice, this list of conditions, the following disclaimer and
|
15
|
+
# attribution to the original authors in the documentation and/or
|
16
|
+
# other materials provided with the distribution.
|
17
|
+
#
|
18
|
+
# - The names of the authors may not be used to endorse or promote
|
19
|
+
# products derived from this software without specific prior
|
20
|
+
# written permission.
|
21
|
+
#
|
22
|
+
# == Disclaimer
|
23
|
+
# This software is provided "as is" and without any express or
|
24
|
+
# implied warranties, including, without limitation, the implied
|
25
|
+
# warranties of merchantability and fitness for a particular purpose.
|
26
|
+
# Authors are not responsible for any damages, direct or indirect.
|
27
|
+
|
28
|
+
module RS
|
29
|
+
|
30
|
+
# Readline-based console UI.
|
31
|
+
#
|
32
|
+
class UI
|
33
|
+
|
34
|
+
# Lib
|
35
|
+
require "readline"
|
36
|
+
|
37
|
+
|
38
|
+
# Default prompt is unexciting.
|
39
|
+
#
|
40
|
+
DefaultPrompt = lambda { "#{Dir.pwd} rs> " }
|
41
|
+
|
42
|
+
|
43
|
+
# UI is set up and prepared to be read/written.
|
44
|
+
#
|
45
|
+
# Initial prompt may be given as an argument, and can
|
46
|
+
# later be changed using #prompt=.
|
47
|
+
#
|
48
|
+
def initialize(initial_prompt = DefaultPrompt)
|
49
|
+
$stdin.sync = true
|
50
|
+
$stdout.sync = true
|
51
|
+
$stderr.sync = true
|
52
|
+
|
53
|
+
# Work around Readline binding to get full line of input
|
54
|
+
Readline.completer_word_break_characters = 0.chr # NUL byte unlikely?
|
55
|
+
Readline.completion_append_character = nil
|
56
|
+
|
57
|
+
self.prompt = initial_prompt
|
58
|
+
|
59
|
+
yield self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Prompt used for next input read.
|
63
|
+
#
|
64
|
+
attr_accessor :prompt
|
65
|
+
|
66
|
+
|
67
|
+
# Signal handlers
|
68
|
+
#
|
69
|
+
# TODO: Perhaps autogenerate these? --rue
|
70
|
+
|
71
|
+
|
72
|
+
# ^C, keyboard interrupt.
|
73
|
+
#
|
74
|
+
def on_SIGINT(&block); @sigint = block; end
|
75
|
+
|
76
|
+
|
77
|
+
# Other event handlers
|
78
|
+
|
79
|
+
# Block to call with each line of input.
|
80
|
+
#
|
81
|
+
def on_input(&block); @input = block; end
|
82
|
+
|
83
|
+
|
84
|
+
# Write #to_s to output.
|
85
|
+
#
|
86
|
+
# TODO: Probably need a .print version, huh? --rue
|
87
|
+
#
|
88
|
+
def puts(data)
|
89
|
+
$stdout.puts data.to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
# Input loop.
|
93
|
+
#
|
94
|
+
# Calls the block given in #on_input for each line of
|
95
|
+
# input, as well as any other #on_* handlers as needed.
|
96
|
+
#
|
97
|
+
def run()
|
98
|
+
loop {
|
99
|
+
begin
|
100
|
+
if input = Readline.readline(@prompt.call, true)
|
101
|
+
@input.call input.chomp
|
102
|
+
else
|
103
|
+
puts ""
|
104
|
+
return
|
105
|
+
end
|
106
|
+
rescue Interrupt
|
107
|
+
@sigint.call
|
108
|
+
end
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|