#--
# ===============================================================================
# Copyright (c) 2005, 2006 Christopher Kleckner
# All rights reserved
#
# This file is part of the Rio library for ruby.
#
# Rio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Rio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Rio; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# ===============================================================================
#++
#
# To create the documentation for Rio run the command
# ruby build_doc.rb
# from the distribution directory. Then point your browser at the 'doc/rdoc' directory.
#
# Suggested Reading
# * RIO::Doc::SYNOPSIS
# * RIO::Doc::INTRO
# * RIO::Doc::HOWTO
# * RIO::Rio
#
# Rio is pre-alpha software.
# The documented interface and behavior is subject to change without notice.
module RIO
class Rio
# Grande Directory Selection Method
#
# Sets the rio to return directories. _args_ can be used to select which directories are returned.
# ario.files(*args) do |f|
# f.directory? #=> true
# end
#
# No aguments selects all directories.
# if _args_ are:
# Regexp:: selects matching directories
# glob:: selects matching directories
# Proc:: called for each directory. the directory is processed unless the proc returns false
# Symbol:: sent to each directory. Each directory is processed unless the symbol returns false
# Fixnum:: matched against the "depth" of the directory
#
# If a block is given, behaves like ario.dirs(*args).each(&block)
#
# See also Rio#files, Rio#entries, Rio#skipdirs
#
# rio('adir').dirs { |frio| ... } # process all directories in 'adir'
# rio('adir').all.dirs { |frio| ... } # same thing recursively
# rio('adir').dirs(/^\./) { |frio| ...} # process dot directories
# rio('adir').dirs[/^\./] # return an array of dot directories
# rio('adir').dirs[:symlink?] # an array of symlinks to directories
#
def dirs(*args,&block) target.dirs(*args,&block); self end
# Grande Directory Exclude Method
#
# If no args are provided selects anything but directories.
# ario.skipdirs do |el|
# el.directory? #=> false
# end
# If args are provided, sets the rio to select directories as with Rio#dirs, but the arguments are
# used to determine which directories will *not* be processed
#
# If a block is given behaves like
# ario.skipdirs(*args).each(&block)
#
# See Rio#dirs
#
# rio('adir').skipdirs { |ent| ... } # iterate through everything except directories
# rio('adir').skipdirs(/^\./) { |drio| ... } # iterate through directories, skipping dot directories
#
#
def skipdirs(*args,&block) target.skipdirs(*args,&block); self end
# Grande Directory Entry Selection Method
#
# No aguments selects all entries.
#
# if +args+ are:
# Regexp:: selects matching entries
# glob:: selects matching entries
# Proc:: called for each entry. the entry is processed unless the proc returns false
# Symbol:: sent to each entry. Each entry is processed unless the symbol returns false
#
# If a block is given, behaves like ario.etries(*args).each(&block)
#
# See also Rio#files, Rio#dirs, Rio#skipentries
#
# rio('adir').entries { |frio| ... } # process all entries in 'adir'
# rio('adir').all.entries { |frio| ... } # same thing recursively
# rio('adir').entries(/^\./) { |frio| ...} # process entries starting with a dot
# rio('adir').entries[/^\./] # return an array of all entries starting with a dot
# rio('adir').entries[:symlink?] # an array of symlinks in 'adir'
#
def entries(*args,&block) target.entries(*args,&block); self end
# Grande Directory Entry Rejection Method
#
# No aguments rejects all entries.
#
# Behaves like Rio#entries, except that matching entries are excluded.
#
def skipentries(*args,&block) target.skipentries(*args,&block); self end
# Grande File Selection Method
#
# Configures the rio to process files. +args+ can be used to select which files are returned.
# ario.files(*args) do |f|
# f.file? #=> true
# end
# No aguments selects all files.
#
# +args+ may be zero or more of the following:
#
# Regexp:: selects matching files
# String:: treated as a glob, and selects matching files
# Proc:: called for each file. the file is processed unless the proc returns false
# Symbol:: sent to each file. Each file is processed unless the symbol returns false
#
# +files+ returns the Rio which called it. This might seem counter-intuitive at first.
# One might reasonably assume that
# rio('adir').files('*.rb')
# would return files. It does not. It configures the rio to return files and returns
# the Rio. This enables chaining for further configuration so constructs like
# rio('adir').all.files('*.rb').norecurse('.svn')
# are possible.
#
# If a block is given, behaves like
# ario.files(*args).each
#
#
# See also Rio#dirs, Rio#entries, Rio#skipfiles
#
# rio('adir').files { |frio| ... } # process all files in 'adir'
# rio('adir').all.files { |frio| ... } # same thing recursively
# rio('adir').files('*.rb') { |frio| ...} # process .rb files
# rio('adir').files['*.rb'] # return an array of .rb files
# rio('adir').files[/\.rb$/] # same thing using a regular expression
# rio('adir').files[:symlink?] # an array of symlinks to files
# rio('adir').files >> rio('other_dir') # copy files to 'other_dir'
# rio('adir').files('*.rb') >> rio('other_dir') # only copy .rb files
#
# For Rios that refer to files, files(*args) causes the file to be processed only if
# it meets the criteria specified by the args.
#
# rio('afile.z').files['*.z'] #=> [rio('afile.z')]
# rio('afile.q').files['*.z'] #=> []
#
# === Example Problem
#
# Fill the array +ruby_progs+ with all ruby programs in a directory and its subdirectories,
# skipping those in _subversion_ (.svn) directories.
#
# ruby_progs = []
#
# For the purposes of this problem, a Ruby program is defined as a file ending with .rb or a file
# that is executable and whose shebang line contains 'ruby':
#
# is_ruby_exe = proc{ |f| f.executable? and f.gets =~ /^#!.+ruby/ }
#
# ==== Solution 1. Use the subscript operator.
#
# ruby_progs = rio('adir').norecurse('.svn').files['*.rb',is_ruby_exe]
#
# Explanation:
#
# 1. Create the Rio
#
# Create a Rio for a directory
# rio('adir')
#
# 2. Configure the Rio
#
# Specify recursion and that '.svn' directories should not be included.
# rio('adir').norecurse('.svn')
# Select files
# rio('adir').norecurse('.svn').files
# Limit to files ending with '.rb'
# rio('adir').norecurse('.svn').files('*.rb')
# Also allow files for whom +is_ruby_exe+ returns true
# rio('adir').norecurse('.svn').files('*.rb',is_ruby_exe)
#
# 3. Do the I/O
#
# Return an array rather than iterating thru them
# ruby_progs = rio('adir').norecurse('.svn').files['*.rb',is_ruby_exe]
#
# ==== Solution 2. Use the copy-to operator
#
# rio('adir').files('*.rb',is_ruby_exe).norecurse('.svn') > ruby_progs
#
# Explanation:
#
# 1. Create the Rio
#
# Create a Rio for a directory
# rio('adir')
#
# 2. Configure the Rio
#
# Select only files
# rio('adir').files
# Limit to files ending with '.rb'
# rio('adir').files('*.rb')
# Also allow files for whom +is_ruby_exe+ returns true
# rio('adir').files('*.rb',is_ruby_exe)
# Specify recursion and that '.svn' directories should not be included.
# rio('adir').files('*.rb',is_ruby_exe).norecurse('.svn')
#
# 3. Do the I/O
#
# Copy the Rio to ruby_progs
# rio('adir').files('*.rb',is_ruby_exe).norecurse('.svn') > ruby_progs
#
# ==== Example Discussion
#
# Note that the only difference between Step 2 of Solution 1 and that of Solution 2 is
# the order of the configuration methods. Step 2 of Solution 1 would have worked equally
# well:
#
# rio('adir').norecurse('.svn').files('*.rb',is_ruby_exe) > ruby_progs
#
# Furthermore if our problem were changed slightly and instead of having our results
# ending up in an array, we wished to iterate through them, we could use:
#
# rio('adir').norecurse('.svn').files('*.rb',is_ruby_exe) { |ruby_prog_rio| ... }
#
# Note the similarities. In fact, solution 1 could have been written:
#
# rio('adir').norecurse('.svn').files('*.rb',is_ruby_exe).to_a
# or
# rio('adir').norecurse('.svn').files('*.rb',is_ruby_exe)[]
#
# Passing the arguments for +files+ to the subscript operator is syntactic sugar.
# The subscript operator does not really take any arguments of its own. It always
# passes them to the most recently called of the grande selection methods (or the
# default selection method, if none have been called). So,
#
# rio('adir').files['*.rb']
# is a shortcut for
# rio('adir').files('*.rb').to_a
#
# and
#
# rio('adir')['*.rb']
# is a shortcut for
# rio('adir').entries('*.rb').to_a
#
# and
#
# rio('afile').lines[0..10]
# is a shortcut for
# rio('afile').lines(0..10).to_a
#
# And so on.
#
#
#
def files(*args,&block) target.files(*args,&block); self end
# Grande File Exclude Method
#
# If no args are provided selects anything but files.
# ario.skipfiles do |el|
# el.file? #=> false
# end
# If args are provided, sets the rio to select files as with Rio#files, but the arguments are
# used to determine which files will *not* be processed
#
# If a block is given behaves like ario.skipfiles(*args).each(&block)
#
# See Rio#files
#
# rio('adir').skipfiles { |ent| ... } # iterate through everything except files
# rio('adir').skipfiles('*~') { |frio| ... } # iterate through files, skipping those ending with a tilde
#
#
def skipfiles(*args,&block) target.skipfiles(*args,&block); self end
# Returns +true+ if the rio is in +all+ (recursive) mode. See Rio#all
#
# adir = rio('adir').all.dirs
# adir.all? # true
# adir.each do |subdir|
# subdir.all? # true
# end
#
# rio('adir').all? # false
#
def all?() target.all?() end
# Grande Directory Recursion Method
#
# Sets the Rio to all mode (recursive)
#
# When called with a block, behaves as if all.each(&block) had been called
#
# +all+ causes subsequent calls to +files+ or +dirs+ to be applied recursively
# to subdirectories
#
# rio('adir').all.files('*.[ch]').each { |file| ... } # process all c language source files in adir
# # and all subdirectories of adir
# rio('adir').all.files(/\.[ch]$/) { |file| ... } # same as above
# rio('adir').files("*.[ch]").all { |file| ... } # once again
# rio('adir').all.files["*.[ch]"] # same, but return an array instead of iterating
#
def all(arg=true,&block) target.all(arg,&block); self end
# Grande Directory Recursion Selection Method
#
# Sets the Rio to recurse into directories like Rio#all. If no args are provided behaves like Rio#all.
# If args are provided, they are processed like Rio#dirs, to select which subdirectories should
# be recursed into. Rio#recurse always implies Rio#all.
#
# +args+ may be one or more of:
# Regexp:: recurse into matching subdirectories
# glob:: recurse into matching subdirectories
# Proc:: called for each directory. The directory is recursed into unless the proc returns false
# Symbol:: sent to each directory. Each directory is recursed into unless the symbol returns false
# Fixnum:: recurse into directories only at the given depth
# Range:: recurse into directories at a range of depths
#
# If a block is given, behaves like ario.recurse(*args).each(&block)
#
# See also Rio#norecurse, Rio#all, Rio#dirs
#
# rio('adir').recurse('test*') { |drio| ... } # process all entries and all entries in subdirectories
# # starting with 'test' -- recursively
#
def recurse(*args,&block) target.recurse(*args,&block); self end
# Grande Directory Recursion Exclude Method
#
# Sets the Rio to recurse into directories like Rio#all. If no args are provided, no
# directories will be recursed into. If args are provided, behaves like Rio#recurse, except
# that matching directories will *not* be recursed into
#
# rio('adir').norecurse('.svn') { |drio| ... } # recurse, skipping subversion directories
#
# rio('adir').norecurse(3) {|drio| ... } # only recurse 2 levels deep into a directory structure
#
def norecurse(*args,&block) target.norecurse(*args,&block); self end
end
end