On 2006.10.12 12:38, Eero S. 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>