Displaying articles with tag

More rubinius & a word about rs

Posted by rue, Sun Feb 11 07:34:00 UTC 2007

I set up a new section for all rubinius-related information. All of it may not get posted on the front page here.

In other news, I took a peek at rs again and was pleasantly surprised at how solid the code looks (considering it was written by yours truly) with only a few exceptions. With more time on my hands again, I will try to get a 0.2 out sometime in the near future.

0 comments | Filed Under: | Tags:

rs 0.1 released!

Posted by rue, Sat Oct 14 00:54:00 UTC 2006

The preliminary release of rs is out! You can view the documentation for more information.

  1. darcs repository
  2. Releases

Using rs

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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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> '/home/me'.to_fso.cd
/home/me> '/tmp'.to_fso.cd
/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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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:
1
2
3
4
5
6
7
8
9
10
11
12
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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:
1
2
3
4
5
6
7
8
9
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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> 

0 comments | Filed Under: rs | Tags:

State of rs tests

Posted by rue, Mon Oct 02 04:36:00 UTC 2006

As mentioned earlier, I was working on converting the rs tests from RSpec to chris2’s Test::Spec based on Test::Unit. The conversion went smoothly apart from some minor extra work in mocking (these should be resolved in the next release). I was able to clean up some things and actually fixed a minor bug—and more than one not-quite-perfect test where the original version could not confidently be said to be correct.

It also put the tests so far in perspective—with BDD, I seem to be more focused on the process of writing code via specifications—which I think I am still using nowhere near to their potential—which is rewarding; but it comes with the price of not testing for failure except for egregious cases. Generally, I am ensuring that the code works with expected input and the corner cases I have thought of but I am not going out of my way to hammer it with random data or known Bad Stuff™. This is definitely something that needs to be improved on in the future.

Some stats on the tests: the working directory I was using is missing some of the newest changes but it clocks in at around 330 lines of Ruby code for the rs libraries with slightly over 1500 lines of tests— an almost 5:1 ratio. There are 76 specifications and a total of 131 requirements (assertions, if you wish)—essentially one assertion for three lines of Ruby code. For your amusement, here is a logfile of a test run with the SpecDox runner.

0 comments | Filed Under: rs | Tags:

ruSH is now rs

Posted by rue, Sun Sep 10 01:45:00 UTC 2006

Someone asked about ruSH on ruby-talk at a time when I had been pondering about making an interim announcement myself. I am working on a Ruby shell again though I deleted all previous work (except from the repository, of course) and have renamed the project rs.

The approach is slightly different this time. The 0.1 release which is hopefully not too far off will feature most of the basic functionality but in pure Ruby syntax. 0.2, eventually, would add a parser for more traditional-looking shell syntax. Currently I am pretty close to 0.1 but I am revisiting the piping (or ObjectStreaming) a bit to add some simplicity :)

Contrived syntax difference example:
1
2
3
4
5
6
7
8
# 0.1 Normal
ls('-la').| .map {|i| i.reverse}  >> '~/revls.txt'

# 0.1 Strict (this should be optional for certain environs)
ls('-la').pipe_to.map {|i| i.reverse}.append_to '~/revls.txt'.to_fso

# 0.2
ls -la | .map {|i| i.reverse} >> ~/revls.txt
The standard stuff, being able to treat files as objects like foo.mp3.play (to some to-be-determined degree) via plugins etc. is still in the plan as well as the rs-enabled system commands and passing stuff around as YAML instead of plain text.

1 comment | Filed Under: rs | Tags: