Eero Saynatkari
10/13/2006 4:26:00 AM
On 2006.10.12 12:38, Eero Saynatkari wrote:
> rs is a project to implement a non-traditional object-oriented shell in Ruby.
And of course I forget to enter the most important part. Examples!
= Examples
Using rs.
== General notes
rs uses Readline which means that you can use the arrow
keys to go up and down in history and back and forth in
the current line.
== Executing Ruby code
You should be able to execute any Ruby code on the line:
rs> 5 + 6
=> 11
rs>
rs> class Foo
..> def bar
..> puts 'Baz'
..> end
..> end
=> nil
rs> Foo.new.bar
Baz
=> nil
rs>
== Output and environment control
You can affect the output using $config values of ruby_return,
prompt and continuation_prompt. $config (and $env) behave like
OpenStructs with the distinction that a method ending with ?
returns a boolean and one ending with ! will set the attribute
to true.
The prompts are #evaled so you can put arbitrary code in there.
Be mindful that a static string has to be enclosed in quotes for
it to work properly.
rs> 1 + 1
=> 2
rs> $config.ruby_return = false
rs> 1 + 1
rs> $config.ruby_return!
=> true
rs>
rs> $config.prompt
=> "'rs> '"
rs> $config.prompt = '"#{Dir.pwd}> "'
=> '"#{Dir.pwd}> "'
/tmp> class Foo
..> end
/tmp> $config.continuation_prompt = $config.prompt
=> '"#{Dir.pwd}> "'
/tmp> class Foo
/tmp> end
/tmp> $config.prompt = "'rs> '"; $config.continuation_prompt = "'..> '"
== FileSystemObjects
FSOs give a relatively object-like interface to files and paths
and incorporate several File, FileUtils, Dir etc. methods.
rs> '/tmp'.to_fso.methods.sort
=> ["/", "<", "<<", "==", "===", "=~", ">", ">>", "__id__", "__send__",
"append_to", "args", "basename", "blockdev?", "cat", "cd", "chardev?",
"chmod", "chmod_R", "chown", "chown_R", "class", "clone", "compare",
"cp", "cp_r", "directory?", "dirname", "display", "dup", "eql?", "equal?",
"exec", "executable?", "executable_real?", "exist?", "exists?", "extend",
"extname", "file?", "find", "freeze", "frozen?", "ftype", "glob", "grpowned?",
"hash", "id", "inspect", "install", "instance_eval", "instance_of?",
"instance_variable_get", "instance_variable_set", "instance_variables",
"is_a?", "kind_of?", "ln", "ln_s", "ln_sf", "lstat", "method", "methods",
"mkdir", "mkdir_p", "mv", "nil?", "object_id", "owned?", "path", "pipe",
"pipe?", "private_methods", "protected_methods", "public_methods", "readable?",
"readable_real?", "readlink", "relative_path", "respond_to?", "rm", "rm_r",
"rm_rf", "rmdir", "run", "send", "setgid?", "setuid?", "singleton_methods",
"size", "size?", "socket?", "split", "stat", "sticky?", "symlink", "symlink?",
"taint", "tainted?", "to_a", "to_os", "to_s", "touch", "truncate", "type",
"umask", "unlink", "untaint", "writable?", "writable_real?", "write_to", "zero?", "|"]
rs>
Generally, these methods behave exactly as their Ruby counterparts with the path
of the FSO given as the file to operate on. For example:
rs> '/tmp'.to_fso.directory?
=> true
rs> '/tmp/quux'.to_fso.exist?
=> false
rs> '/tmp'.to_fso.cd {'./quux'.to_fso.touch}
=> nil
rs> '/tmp/quux'.to_fso.exist?
=> true
rs> '/tmp/quux'.to_fso.rm
=> ['/tmp/quux']
rs> '/tmp/quux'.to_fso.exist?
=> false
You could of course put the FSO in a variable to avoid the repetition--also, if
you feel like metaprogramming a bit, you could put a String#method_missing in your
~/.rsrc so that you can skip the #to_fso (which will eventually go away, of course).
== Executing programs
FSOs containing executable files may (unsurprisingly) be executed. One thing to
know about the processing of FSOs is that currently any filename that does not
start with ./, ../, / or ~/ is considered to be 'unqualified' and must exist in
$PATH. In addition to this, unknown methods at the top-level are first treated
as unqualified files (falling back on normal if not found). The UI provides
special handling and will automatically run executables. Arguments may also be
given.
rs> 'ls'.to_fso.run
...
=> nil
rs> ls
...
=> nil
rs> ls '-la'
...
=> nil
rs> 'ls'.to_fso.args('-l')
...
=> nil
rs>
== Input redirection
More or less arbitrary objects can be 'redirected', > indicating overwriting
and >> appending. In both cases, the file will be created if it does not exist.
rs> '/tmp/foo'.to_fso.touch
=> ["/tmp/foo"]
rs> '/tmp/foo'.to_fso.cat
=> nil
rs> '/tmp/foo'.to_fso < "Foo"
=> 3
rs> '/tmp/foo'.to_fso.cat
Foo
=> nil
rs> '/tmp/foo'.to_fso << 45
=> 2
rs> '/tmp/foo'.to_fso.cat
Foo45
=> nil
rs> '/tmp/nonexist'.to_fso < '/tmp/foo'.to_fso.read
=> 5
rs> '/tmp/nonexist'.to_fso.cat
Foo45
=> nil
rs>
The opposite should also work:
rs> '/tmp/nonexist'.to_fso.cat
Foo45
=> nil
rs> 78 >> '/tmp/nonexist'.to_fso
=> 2
rs> '/tmp/nonexist'.to_fso.cat
Foo4578
=> nil
rs>
There are a few exceptions. If the 'input' is an Array, it is recursively
joined with newlines. If the input is an executable FSO, it will be run
and the result written as a String. Thirdly, if the file TO which the input
is going is executable, it is converted to an ObjectStream instead. This
brings us to our next topic.
== Pipes
Executable programs (and/or static input) can be chained together to
an arbitrary degree using ObjectStreams, also known as pipes.
The result of a piping operation can be queried with #result (this is
done automatically by the UI if the value of the expression is an OS).
Alternatively, an iterator interface is exposed with #each (an other
Enumerable methods).
A few modifications take place on a Ruby object being piped: Arrays are
newline-joined, #to_proc objects are #called and everything else is set
to its #to_s representation.
rs> ls | wc
8 8 46
=> nil
rs> (ls | wc).result
=> [" 8 8 46"]
rs> "foo\nbar" | wc('-l')
2
=> nil
rs> "foo\nbar" | wc('-l')
2
=> nil
rs> (ls | tr('a-z A-Z')).each {|f| p f.reverse}
"ELIFEKAR"
"SCRAD_"
"NIB"
"OOB"
"COD"
"BIL"
"BR.PUTES"
"TSET"
=> #<IO:0x65be40>
rs> lambda {"foo\nbar"} | wc
2 2 8
=> nil
rs>