[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