== Overview
Rio is a Ruby I/O convenience class wrapping much of the functionality
of IO, File and Dir. Rio also uses FileUtils, Tempfile, StringIO,
OpenURI, Zlib, and CSV to provide similar functionality using a simple
consistent interface. In addition to forwarding the interfaces
provided by IO, File, and Dir to an appropriate object, Rio provides a
"grande" interface that allows many common application-level I/O and
file-system tasks to be expressed succinctly.
== SYNOPSIS
For the following assume:
astring = ""
anarray = []
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') < astring # copy
rio('afile') << astring # append
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') << rio('afile')
Copy a directory structure to another directory
rio('adir') >> rio('another_directory')
Copy a web-page to a file
rio('http://rubydoc.org/') > rio('afile')
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
Copy a gzipped file un-gzipping it
rio('afile.gz').gzip > rio('afile')
Copy a plain file, gzipping it
rio('afile.gz').gzip < rio('afile')
Copy a file from a ftp server into a local file un-gzipping it
rio('ftp://host/afile.gz').gzip > rio('afile')
Iterate over the entries in a directory
rio('adir').entries { |entrio| ... }
Iterate over only the files in a directory
rio('adir').files { |entrio| ... }
Iterate over only the .rb files in a directory
rio('adir').files('*.rb') { |entrio| ... }
Iterate over .rb files but not symlinks to .rb files
rio('adir').files('*.rb').skip(:symlink?) { |entrio| ... }
Iterate over only the _dot_ files in a directory
rio('adir').files(/^\./) { |entrio| ... }
Iterate over the files in a directory and its subdirectories, skipping
'.svn' and 'CVS' directories
rio('adir').norecurse(/^\.svn$/,'CVS').files { |entrio| ... }
Create an array of the .rb entries in a directory
anarray = rio('adir')['*.rb']
Create an array of the .rb entries in a directory and its
subdirectories.
anarray = rio('adir').all['*.rb']
Iterate over the .rb files in a directory and its subdirectories
rio('adir').all.files('*.rb') { |entrio| ... }
Iterate over the non-empty, non-comment chomped lines of a file
rio('afile').chomp.skip.lines(: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').skip.lines(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
Create a symbolic link 'asymlink' in 'adir' which refers to
'adir/afile'
rio('adir/afile').symlink('adir/asymlink')
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")
== New for version 0.3.4
* New Grande Selection parameter.
A major weakness of Rio's selection methods (lines, files, etc.)
has always been that it only implemented a logical OR.
rio('afile').lines(0..10,/Rio/) {...}
iterates through lines that are in the range 0..10 OR
contain 'Rio'.
rio('adir').files(:executable?,'*.rb') {...}
iterates through files that are executable OR match '*.rb'
Selecting files that matched both required using a proc.
rio('adir').files(proc{ |f| f.executable? and f.fnmatch?('*.rb')})
{...}
Rio's grande selection methods will now accept an array of conditions
which must all be matched, in order to be selected. A logical AND.
rio('adir').files([:executable?,'*.rb']) {...}
The array, of course, need not be the only paramter.
rio('adir').files('*.exe',[:executable?,'*.rb']) {...}
selects .exe files and .rb files that are executable.
* Renamed some of grande rejection methods.
(based on a suggestion by Gavin Sinclair)
nolines => skiplines
nofiles => skipfiles
etc.
* New skip() grande method
rio('afile').skip.lines(/Rio/) # same as skiplines(/Rio/)
rio('afile').lines(/Rio/).skip(0..9) # lines with 'Rio', exclude
# the first ten lines
* Alternative syntaxes for creating Rios that have no path.
rio(?-) # create a Rio refering to stdio
rio(:stdio) # same thing.
rio.stdio # same thing
RIO.stdio # ditto
RIO::Rio.stdio # once again
* From Pathname added
* root?
* mountpoint?
* realpath
* cleanpath
* Removed Rio#slurp in favor of Rio#contents.
* Added aliases for the copy operators. (suggested by Dave Burt)
* copy_to >
* append_to >>
* copy_from <
* append_from <<
* Bug fixes and corrections
Project:: http://rubyforge.org/projects/rio/
Documentation:: http://rio.rubyforge.org/
Bugs:: http://rubyforge.org/tracker/?group_id=821
Email:: rio4...@rubyforge.org
== Copyright
Copyright (c) 2005, Christopher Kleckner. All rights reserved
== License
Rio is released under the GNU General Public License
(http://www.gnu.org/licenses/gpl.html)
-Christopher Kleckner
This looks super sweet. My teeth hurt!
I also want to say that your announcement, with all the examples, as
well as those by Ara T. Howard (see his Traits release announcements),
are exemplary.
Really great!
Thanks,
James
--
http://www.ruby-doc.org - The Ruby Documentation Site
http://www.rubyxml.com - News, Articles, and Listings for Ruby & XML
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
#New and Improved -- Rio 0.3.4
#
fwiw, rio+rush+ruby will kill window's monad even before it's announced
thanks for rio.
kind regards -botp
Then again, I've seen a disturbing trend of people ignoring anything that isn't in their blackberry. Is that it? Not in e-mail, doesn't exist?
Just curious which people prefer: examples in the announce or refer to a web page where examples are to be found?
Zed A. Shaw
http://www.zedshaw.com/
On Wed, 7 Sep 2005 11:30:29 +0900
James Britt <jam...@neurogami.com> wrote:
> > Rio is a Ruby I/O convenience class wrapping much of the functionality
> > of IO, File and Dir. Rio also uses FileUtils, Tempfile, StringIO,
> > OpenURI, Zlib, and CSV to provide similar functionality using a simple
> > consistent interface. In addition to forwarding the interfaces
> > provided by IO, File, and Dir to an appropriate object, Rio provides a
> > "grande" interface that allows many common application-level I/O and
> > file-system tasks to be expressed succinctly.
<snip>
Examples in the email. Entice me.
I'm usually stunned that such examples exist at all. After that, I
really don't care where the author puts them.
I appreciate the time and effort spent on code, and the extra time and
effort spent on docs, so see no reason to be concerned.
But I'm willing to cut people some slack when they're offering free
software.
+1
Yes, I love documentation by example, and showing what a class/module/
library does by showing it in action is similar to the "picture worth
a thousand words", for me.
The announcement should, of course, ALSO refer to a web page where
more examples and rigorous documentation exist. But the teaser is
pleasing, to me.
Zed A. Shaw
http://www.zedshaw.com/
> New and Improved -- Rio 0.3.4
>
> [ ... ]
This looks great!
However, I'm seeing a problem. I installed this via "gem install rio",
but I get an error when I try to run it. Does anyone know how I
can prevent the error below?
Thanks in advance.
% gem list rio
*** LOCAL GEMS ***
rio (0.3.4)
Rio - Ruby I/O Comfort Class
% echo abc >/tmp/abc
% cat /tmp/abc
abc
% irb
irb(main):001:0> require_gem 'rio'
=> true
irb(main):002:0> rio('/tmp/abc') > rio('/tmp/xyz')
NameError: uninitialized constant RIO::Ops::FileOrDir::ExistOrNot
from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/ops/file.rb:62
from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in
`require__'
from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in
`require'
from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/file.rb:39
from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in
`require__'
from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in
`require'
from
/usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/factory.rb:197:in
`state2class'
from
/usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/factory.rb:206:in
`try_state_proc'
from
/usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/factory.rb:204:in `[]'
from
/usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/state.rb:120:in `become'
from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/path.rb:80:in
`efile'
from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/path.rb:65:in
`when_missing'
from
/usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/state.rb:134:in
`method_missing'
from
/usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/state.rb:136:in
`__send__'
from
/usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/state.rb:136:in
`method_missing'
from
/usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/if/grande.rb:402:in `>'
from (irb):2irb(main):003:0>
irb(main):004:0*
--
Lloyd Zusman
l...@asfast.com
God bless you.
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require_gem 'rio'
=> true
irb(main):003:0> rio('/tmp/abc') > rio('/tmp/xyz')
=> #<Rio:0x812ed94:"file:///tmp/abc" (Stream::Close)>
% cat /tmp/xyz
abc
%
The only difference between mine and yours, is "require 'rubygems'",
and when I leave that off I get an entirely different error:
irb(main):001:0> require_gem 'rio'
NoMethodError: undefined method `require_gem' for main:Object
from (irb):1
irb(main):002:0> rio('/tmp/abc') > rio('/tmp/xyz')
NoMethodError: undefined method `rio' for main:Object
from (irb):2
I admit that I have done very little testing on the gem installation
of Rio. If you figure this out please let me know, so I can document
it for other gem users.
Cheers,
-Christopher
> Lloyd Zusman wrote:
> > % irb
> > irb(main):001:0> require_gem 'rio'
> > => true
> > irb(main):002:0> rio('/tmp/abc') > rio('/tmp/xyz')
> > NameError: uninitialized constant RIO::Ops::FileOrDir::ExistOrNot
> > from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/ops/file.rb:62
> > from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in
> ....
> I can not reproduce this. My irb session looks like this:
>
> irb(main):001:0> require 'rubygems'
> => true
> irb(main):002:0> require_gem 'rio'
> => true
> irb(main):003:0> rio('/tmp/abc') > rio('/tmp/xyz')
> => #<Rio:0x812ed94:"file:///tmp/abc" (Stream::Close)>
> % cat /tmp/xyz
> abc
> %
>
> The only difference between mine and yours, is "require 'rubygems'",
> and when I leave that off I get an entirely different error:
>
> irb(main):001:0> require_gem 'rio'
> NoMethodError: undefined method `require_gem' for main:Object
> from (irb):1
> irb(main):002:0> rio('/tmp/abc') > rio('/tmp/xyz')
> NoMethodError: undefined method `rio' for main:Object
> from (irb):2
>
> I admit that I have done very little testing on the gem installation
> of Rio. If you figure this out please let me know, so I can document
> it for other gem users.
>
> Cheers,
> -Christopher
Thanks for looking into this. I'll keep testing and see if I can
isolate the
problem.
By the way, the reason that I didn't need "require 'rubygems'" is because
I have the RUBYOPT environment variable set to "rubygems". If I unset
this variable and do a "require 'rubygems'" before the "require_gem 'rio'"
command, I get the same error.
Also, in case this might shed some light on my problem ...
% ruby --version
ruby 1.8.2 (2004-12-25) [i686-linux]
% gem --version
0.8.11
Prior to this, I had earlier problems with gems, and so I completely
uninstalled ruby (I had been using the 1.9.x version from cvs), and I
re-installed it from the official 1.8.2 version, totally from scratch with
none of my old libraries, scripts, or gems present on my system. It
was only then that I installed "gem", and then "rio".
Thanks again for any help you folks might be able to give me on this.
I, too, have the problem and had ruby-1.9 before.
My test program:
#!/usr/bin/env ruby
require 'rio'
$VERBOSE = true
rio('/etc/passwd') > rio('/tmp/passwd')
This worked fine with rio-0.3.3 but fails with 0.3.4.
I have attached the output, for what it's worth.
wybo>ruby -v
ruby 1.8.2 (2004-12-25) [i686-linux]
wybo>gem -v
0.8.11
--
Wybo
> Lloyd Zusman wrote:
>> Prior to this, I had earlier problems with gems, and so I completely
>> uninstalled ruby (I had been using the 1.9.x version from cvs), and I
>> re-installed it from the official 1.8.2 version, totally from scratch with
>> none of my old libraries, scripts, or gems present on my system. It
>> was only then that I installed "gem", and then "rio".
>
> I, too, have the problem and had ruby-1.9 before.
I found a work-around for this problem, but I don't understand why it
fixes it, and why the module wasn't functioning properly in the first
place.
After looking this over, do any of you have any idea why this was
failing, and why this fix worked?
As you can see from the stack trace at the bottom of this message, which
both Wybo and I were getting, the "ExistOrNot" module was not found.
However, moving the following code fragment closer to the top of the
following file seems to fix the problem.
The file:
.../gems/rio-0.3.4/lib/rio/ops/either.rb
The code fragment:
module ExistOrNot
end
module Existing
include ExistOrNot
end
module NonExisting
include ExistOrNot
end
Here's my patch which fixes this:
*** either.rb.orig Sat Sep 10 09:13:51 2005
--- either.rb Sat Sep 10 09:15:11 2005
***************
*** 54,57 ****
--- 54,67 ----
module Ops
module FileOrDir
+
+ module ExistOrNot
+ end
+ module Existing
+ include ExistOrNot
+ end
+ module NonExisting
+ include ExistOrNot
+ end
+
module Existing
def chmod(mod) rtn_self { Impl::U.chmod(mod,fspath) } end
***************
*** 117,129 ****
end
-
- module ExistOrNot
- end
- module Existing
- include ExistOrNot
- end
- module NonExisting
- include ExistOrNot
- end
end
end
--- 127,130 ----
Wybo Dekker <wy...@servalys.nl> writes:
> My test program:
>
> [ ... etc. ... ]
>
> wybo>cat riotest
> #!/usr/bin/env ruby
>
> require 'rio'
> $VERBOSE = true
>
> rio('/etc/passwd') > rio('/tmp/passwd')
> wybo>riotest
> /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/cp.rb:190: warning: useless use of < in void context
> /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/cp.rb:290: warning: useless use of > in void context
> /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/ops/file.rb:62: uninitialized constant RIO::Ops::FileOrDir::ExistOrNot (NameError)
> from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require__'
> from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/file.rb:39
> from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require__'
> from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in `require'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/factory.rb:197:in `state2class'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/factory.rb:206:in `try_state_proc'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/factory.rb:204:in `[]'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/state.rb:120:in `become'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/path.rb:80:in `efile'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/path.rb:65:in `when_missing'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/state.rb:134:in `method_missing'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/state.rb:136:in `__send__'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/state.rb:136:in `method_missing'
> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/if/grande.rb:402:in `>'
> from ./riotest:6
>
> [ ... ]
Cheers,
-Christopher
I'm glad to help :)
I'm wondering if this behavior has something to do with the way that the
gem system evaluates files that it includes. This might have some
subtle idiosyncracies.
But anyway, it works now, and that's what's most important. All's well
that ends well!
Thanks.
> ruby t.rb
/usr/local/lib/ruby/site_ruby/1.8/rio/ops/either.rb:110:in `require': No
such file toload -- Pathname (LoadError)
from /usr/local/lib/ruby/site_ruby/1.8/rio/ops/either.rb:110
from /usr/local/lib/ruby/site_ruby/1.8/rio/ops/file.rb:61:in
`require'
from /usr/local/lib/ruby/site_ruby/1.8/rio/ops/file.rb:61
from /usr/local/lib/ruby/site_ruby/1.8/rio/file.rb:39:in `require'
from /usr/local/lib/ruby/site_ruby/1.8/rio/file.rb:39
from /usr/local/lib/ruby/site_ruby/1.8/rio/factory.rb:197:in
`require'
from /usr/local/lib/ruby/site_ruby/1.8/rio/factory.rb:197:in
`state2class'
from /usr/local/lib/ruby/site_ruby/1.8/rio/factory.rb:206:in
`try_state_proc'
... 7 levels...
from /usr/local/lib/ruby/site_ruby/1.8/rio/state.rb:136:in
`__send__'
from /usr/local/lib/ruby/site_ruby/1.8/rio/state.rb:136:in
`method_missing'
from /usr/local/lib/ruby/site_ruby/1.8/rio/if/grande.rb:402:in `>'
from t.rb:4
where t.rb is...
require 'rio'
str= ""
rio('t.asv') > str
puts "#{str}"
rio4ruby wrote:
>Lloyd Zusman wrote:
>
>
>>% irb
>>irb(main):001:0> require_gem 'rio'
>>=> true
>>irb(main):002:0> rio('/tmp/abc') > rio('/tmp/xyz')
>>NameError: uninitialized constant RIO::Ops::FileOrDir::ExistOrNot
>> from /usr/local/lib/ruby/gems/1.8/gems/rio-0.3.4/lib/rio/ops/file.rb:62
>> from /usr/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:21:in
>>
>>
>....
The "pathname" library comes with Ruby. Perhaps rio was tested
on Windows only, which ignores case in file names. Thus, on
Windows both work: require 'pathname and require 'Pathname',
but on Linux, only the former works (since Linux file systems
are case sensitive).
HTH,
Stefan
Cheers
-Christopher