[Rd] Lightweight 'package' idea.

Keith Jewell k.jewell at campden.co.uk
Wed Jan 13 18:03:54 CET 2010


Going back a few months....
I also thought "it would be nice if R had built into it some way of running 
code in source packages possibly with degraded functionality to ease 
development"
so building on Barry Rowlingsons start I came up with this:
---------------------------
loadDir <- function(.Root = choose.dir(getwd())){
   require(tools, quietly=TRUE) # for Rd2HTML
  .Package = basename(.Root)  # package name defined by directory name
  while(sum(search()==.Package)>0) detach(pos=which(search()==.Package)[1]) 
# if already attached, detach
  attach(NULL, name=.Package) # attach empty environment
  assign(".Root", .Root, pos=.Package) # insert .Root into .Package
# create reloadDir function and add into .Package
  reloadDir <- function(.Package) {     # .Package must be character
    for (f in list.files(path=file.path(get(".Root", pos=.Package) , "R"), 
# path is .Root/R/
       pattern=".R$",full.names=TRUE,recursive=TRUE,ignore.case=TRUE)) 
# file type = .R
       sys.source(f, envir=as.environment(.Package))       # source all such 
files into .Package
       invisible(.Package)} # invisibly return package name
  assign(x = "reloadDir", value = reloadDir, pos=.Package)
# create help.'.Package' function and add into package
#   (because function is created here, current value of .Root is in its 
environment)
  help.Package <- function(subj=""){    # default subject is blank
      if (substitute(subj)=="") subj = "*" else subj <- paste("*", 
substitute(subj), "*", sep="")  # get *subj*' as character
      hfile <- list.files(path=file.path(.Root, "man"), # path is .Root/man/
        pattern=paste(subj,"Rd$", sep="."),           # file = subj.Rd
        full.names=TRUE,recursive=TRUE,ignore.case=TRUE) # list of matching 
files
      if (length(hfile) != 1)       # if not exactly one file, choose one
        hfile <- choose.files(file.path(.Root, "man", paste(subj,"Rd", 
sep=".")), multi=FALSE)
      if(hfile != "") {     # if exactly one file, open it
        if(length(grep(".Rd$", hfile, ignore.case=TRUE)) ==1) { # if it's an 
Rd file, create/update html
           outfile <- sub("Rd$", "html", hfile, ignore.case=TRUE)  # name of 
corresponding html
           Rd2HTML(hfile, sub("Rd$", "html", hfile, ignore.case=TRUE), 
.Package)   # convert Rd to html
           hfile <- sub("Rd$", "html", hfile, ignore.case=TRUE)} # point to 
html
        shell.exec(shQuote(hfile))} # use operating system to open file of 
any type
        invisible(hfile)}    # invisibly return help file name
  assign(x = paste("help", .Package, sep="."), value=help.Package, 
pos=.Package)
# use the reloadDir function to populate the environment
  reloadDir(.Package)
  invisible(.Package)  # invisibly return .Package name
   }
---------------------------
and a corresponding .Rd file
----------------------------
\name{loadDir}
\alias{reloadDir}
\alias{help.Package}
\title{Load an unbuilt package}
\description{
Loads code from a packages \file{\\R} subdirectory and gives access to help 
files in the packages
 \file{\\man} subdirectory (translating \file{.Rd} files to \file{.html}).
}
\usage{
loadDir(.Root = choose.dir(getwd()))
reloadDir(.Package)
help.'.Package'(subj="")
}
\arguments{
  \item{.Root}{character, scalar. The package directory (containing 
subdirectories \file{\\R} and \file{\\man}). \cr
            \code{basename(.Root)} is taken as the package name 
(\code{.Package}).}
  \item{.Package}{character, scalar. Package name to be reloaded}
  \item{subj}{character, scalar. File name to be searched for in 
\file{\\man} subdirectory}
}
\details{ \describe{
\item{\code{loadDir}}{ attaches an environment at the second position on the 
search list with name \code{basename(.Root)} (after detaching any existing 
entries with that name). Into that environment it sources all \file{.R} 
files in the \file{\\R} subdirectory, searching recursively. \cr
In that environment it also places \code{.Root} and \code{.Package 
<-basename(.Root)} so that \code{get(".Root", pos=.Package)} can be used to 
retrieve the original file path. \cr
In that environment it also places function \code{reloadDir} (q.v.) \cr
In that environment it also places a function named '\code{help.}' followed 
by the name of the package (see \code{help.'.Package'})}
\item{\code{reloadDir}}{ re-sources all \file{.R} files in the packages 
\file{\\R} subdirectory, searching recursively.}
\item{\code{help.'.Package'}}{ recursively searches the packages 
\file{\\man} subdirectory for files named \code{subj}, initially searching 
for files of type \file{.Rd}. \cr
If there is not exactly one such file it opens a \code{file.choose} dialog 
to choose a single file of any type. \cr
If the single file is of type \file{.Rd} it is translated to a 
correspondingly named \file{.html} file in the same folder, which is opened 
by the operating system's file associations. \cr
If the file chosen is of any other type it is opened by the operating 
system's file associations.} }}
\value{ \describe{
 \item{\code{loadDir}}{  \code{invisible(.Package)}, scalar, character}
 \item{\code{reloadDir}}{  \code{invisible(.Package)}, scalar, character}
 \item{\code{help.'.Package'}}{\code{invisible}, scalar, character; file 
opened (the \file{.html} file if a \file{.Rd} file was chosen)}}
}
\references{
%% ~put references to the literature/web site here ~
}
\author{
%%  ~~who you are~~
}
\note{
%%  ~~further notes~~
}

%% ~Make other sections like Warning with \section{Warning }{....} ~

\seealso{
%% ~~objects to See Also as \code{\link{help}}, ~~~
}
\examples{
\dontrun{
 loadDir() navigating to '\\\\Server02\\stats\\R\\CBRIutils' adds the 
package CBRIutils
   then
 reload("CBRIutils") re-sources all '\\R\\*.R' files.
   and
 help.CBRIutils(item) converts '\\man\\item.Rd' to '\\man\\item.html' which 
it opens in the default web browser.}}
% Add one or more standard keywords, see file 'KEYWORDS' in the
% R documentation directory.
\keyword{ ~kwd1 }
\keyword{ ~kwd2 }% __ONLY ONE__ keyword per line
-----------------------------------

No guarantees or warranties of any kind, but perhaps people will find it 
useful.
I'm sure real R experts will be able to improve it....

Regards,

Keith J
============================
"Gabor Grothendieck" <ggrothendieck at gmail.com> wrote in message 
news:971536df0908210532k1152976al404b94a230f9858e at mail.gmail.com...
That's nifty.  Perhaps it could look into

   /foo/bar/baz/lib1/*/R

in which case one could simply place source
packages in /foo/bar/baz/lib1

In fact it would be nice if R had built into it some way
of running code in source packages possibly with
degraded functionality to ease development, i.e.
if one added /foo/bar/baz/lib1 to .libPaths and if xx
were a source package in /foo/bar/baz/lib1 then
one could use library(xx) and use xx functions directly,
possibly with degraded functionality, e.g. no help files.

On Fri, Aug 21, 2009 at 8:03 AM, Barry
Rowlingson<b.rowlingson at lancaster.ac.uk> wrote:
> I'm often wanting to develop functions whilst manipulating data. But I
> don't want to end up with a .RData full of functions and data. It
> might be that I have functions that are re-usable but not worth
> sticking in a package.
>
> So I've tried to come up with a paradigm for function development
> that more closely follows the way Matlab and Python do it (partly
> inspired by a confused Matlab convert over on R-help).
>
> My requirements were thus:
>
> * .R files as the master source for R functions
> * Don't see the functions in ls()
> * After editing R, make it easy to update the definitions visible to
> R (unlike rebuilding and reloading a package).
>
> So I wrote these two in a few mins:
>
> loadDir <- function(dir){
> e = attach(NULL,name=dir)
> assign("__path__",dir,envir=e)
> reloadDir(e)
> e
> }
>
> reloadDir <- function(e){
> path = get("__path__",e)
> files = 
> list.files(path,".R$",full.names=TRUE,recursive=TRUE,ignore.case=TRUE)
> for(f in files){
> sys.source(f,envir=e)
> }
> }
>
> Usage is something like:
>
> lib1 = loadDir("/foo/bar/baz/lib1/")
>
> - it creates a new environment on the search path and sources any .R
> it finds in there into that environment. If you edit anything in that
> directory, just do reloadDir(lib1) and the updated definitions are
> loaded. It's like python's "import foo" and "reload(foo)".
>
> Sourcing everything on any change seems a bit wasteful, but until R
> objects have timestamps I can't think of a better way. Hmm, maybe my
> environment could keep a __timestamp__ object... Okay, this is getting
> less simple now...
>
> So anyway, have I done anything wrong or stupid here, or is it a
> useful paradigm that seems so obvious someone else has probably done
> it (better)?
>
> Barry
>
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>



More information about the R-devel mailing list