Monday, July 30, 2007

Why Rio?

Ever set out to write a simple little program only to find that hours or days later you have spent most of your time dealing with code that has nothing to do with the problem you were trying to solve? Ever get to that point and not even remember what you were trying to do in the first place? Ever find yourself mumbling obscenities about people who don't write documentation for their code even though you don't write documentation for your own?


The first time I saw Ruby I was in awe of her beauty -- and she was so smart! It was as if she could read my mind. I would write:

3.times { print "Hello Ruby" }

and she would print "Hello Ruby" 3 times.

She really understood me.

I had dated her older cousin Perl for several years. While Perl was resourceful, she was not much to look at. And while she tried so hard to understand things like objects, it always seemed like she was faking it. She pretended to understand them only because she knew that was what I wanted. She never really got it.

Being so enamored of Ruby I wanted to take her out in public. I was stunned to find that when I tried to have her do common tasks, like dealing with files, directories and streams, she was as clumsy as her cousin. Sometimes she acted like an object and other times she made me treat her like a function. Her I/O interface was spread across File, Dir, IO, and Pathname. Sometimes available through instance methods and other times using class methods. In many ways she reminded me of her grandfather, C. I was getting confused.

I thought the problem must be with me and decided to seek more documentation. The only thing I could find was
Why's Poignant Guide... now I was really confused.

I decided I must find a way to help Ruby be as beautiful when dealing with the public as she was with me in private; to make her Input/Output operations so simple that they would be an afterthought; so that I and others could concentrate on the problem we were trying to solve rather than mundane things like I/O. I also decided to document my work well -- with lots of examples.

I remembered a pretty dress I saw Perl wear once called IO::All. Using that as a starting point, I tried to design a gown that, when adorning Ruby's natural beauty, would make common I/O operations as simple and painless as they should be.

Saturday, July 7, 2007

Rio 0.4.1 Released

New
  • Includes fix for bug involving file system paths with special characters.
  • Other minor fixes.

Rio - Ruby I/O Facilitator

fa-cil-i-tate: To make easy or easier.

Overview

Rio is a facade for most of the standard ruby classes that deal with I/O; providing a simple, intuitive, succinct interface to the functionality provided by IO, File, Dir, Pathname, FileUtils, Tempfile, StringIO, OpenURI and others. Rio also provides an application level interface which allows many common I/O idioms to be expressed succinctly.

SYNOPSIS

For the following assume:
astring = ""
anarray = []

Iterate over the .rb files in a directory.
rio('adir').files('*.rb') { |entrio| ... }

Return an array of the .rb files in a directory.
anarray = rio('adir').files['*.rb']

Copy the .rb files in a directory.to another directory.
rio('adir').files('*.rb') > rio('another_directory')

Iterate over the .rb files in a directory and its subdirectories.
rio('adir').all.files('*.rb') { |entrio| ... }

Return an array of the .rb files in a directory and its
subdirectories.
anarray = rio('adir').all.files['*.rb']

Copy or append a file to a string
rio('afile') > astring # copy
rio('afile') >> astring # append

Copy or append a string to a file
rio('afile') <>

Copy or append the lines of a file to an array
rio('afile') > anarray
rio('afile') >> anarray

Copy or append a file to another file
rio('afile') > rio('another_file')
rio('afile') >> rio('another_file')

Copy a file to a directory
rio('adir') <<>

Copy a directory to another directory
rio('adir') >> rio('another_directory')

Copy a web-page to a file
rio('http://rubydoc.org/') > rio('afile')

Read a web-page into a string
astring = rio('http://rubydoc.org/').read

Ways to get the chomped lines of a file into an array
anarray = rio('afile').chomp[] # subscript operator
rio('afile').chomp > anarray # copy-to operator
anarray = rio('afile').chomp.to_a # to_a
anarray = rio('afile').chomp.readlines # IO#readlines

Iterate over selected lines of a file
rio('adir').lines(0..3) { |aline| ... } # a range of lines
rio('adir').lines(/re/) { |aline| ... } # by regular expression
rio('adir').lines(0..3,/re/) { |aline| ... } # or both

Return selected lines of a file as an array
rio('adir').lines[0..3] # a range of lines
rio('adir').lines[/re/] # by regular expression
rio('adir').lines[0..3,/re/] # or both

Iterate over selected chomped lines of a file
rio('adir').chomp.lines(0..3) { |aline| ... } # a range of lines
rio('adir').chomp.lines(/re/) { |aline| ... } # by regular expression

Return selected chomped lines of a file as an array
rio('adir').chomp[0..3] # a range of lines
rio('adir').chomp[/re/] # by regular expression

Copy a gzipped file un-gzipping it
rio('afile.gz').gzip > rio('afile')

Copy a plain file, gzipping it
rio('afile.gz').gzip <>

Copy a file from a ftp server into a local file un-gzipping it
rio('ftp://host/afile.gz').gzip > rio('afile')

Return an array of .rb files excluding symlinks to .rb files
rio('adir').files('*.rb').skip[:symlink?]

Put the first 10 chomped lines of a gzipped file into an array
anarray = rio('afile.gz').chomp.gzip[0...10]

Copy lines 0 and 3 thru 5 of a gzipped file on an ftp server to stdout
rio('ftp://host/afile.gz').gzip.lines(0,3..5) > ?-

Return an array of files in a directory and its subdirectories,
without descending into .svn directories.
rio('adir').norecurse(/^\.svn$/).files[]

Iterate over the non-empty, non-comment chomped lines of a file
rio('afile').chomp.skip(:empty?,/^\s*#/) { |line| ... }

Copy the output of th ps command into an array, skipping the header
line and the ps command entry
rio(?-,'ps -a').skiplines(0,/ps$/) > anarray

Prompt for input and return what was typed
ans = rio(?-).print("Type Something: ").chomp.gets

Change the extension of all .htm files in a directory and its
subdirectories to .html
rio('adir').rename.all.files('*.htm') do |htmfile|
htmfile.extname = '.html'
end

Copy a CSV file, changing the separator to a semicolon
rio('comma.csv').csv > rio('semicolon.csv').csv(';')

Iterate through a CSVfile with each line parsed into an array
rio('afile.csv').csv { |array_of_fields| ...}

Create a tab separated file of accounts in a UNIX passwd file,
listing only the username, uid, and realname fields
rio('/etc/passwd').csv(':').columns(0,2,4) > rio('rpt').csv("\t")

Pipe multiple commands
rio('afile') | rio(?-,'acmd') | 'another_cmd' | ?-

Contact

Project:: http://rubyforge.org/projects/rio/
Documentation:: http://rio.rubyforge.org/
Bugs:: http://rubyforge.org/tracker/?group_id=821



Copyright (c) 2005,2006,2007 Christopher Kleckner.
All rights reserved

Rio is released under the GNU General Public License
(http://www.gnu.org/licenses/gpl.html).