Thursday, April 4, 2019

New TempLS v4 preamble - a documentation system.


Background

I'm gearing up to release TempLS V4. I have been working on this (and am now using it) to manage the extra demands of GHCN V4. A complication is that I now use a lot of my own library functions in R. For V3 I could replace them, but now they are too heavily embedded. So I'll hneed to supply the library as well. This works like an R package, but has a different documentation system, which I'll now describe. I think it is better, and I'll use it for the code of TempLS V4 as well. There is an example of the system at the end, and following that, a zipfile with all the entities involved, including the R code.

Motivation for a different system

I write a lot of R code, and I have a set of functions that I currently load in to be part of the working environment. I also have a library of Javascript functions that I import. I need a scheme where these are kept in some organising system with associated documentation. I would like this to be very easily edited, and then generated as HTML for easy reference.

The classic R way of doing this is to create and attach a package. This is a list of parsed functions that are not in the working environment, but sit on the search path at a lower level. That means that they don't overwrite existing names, but those names in the environment might block access to the package functions. R has a system for documenting using an associated .rd file. This has a Latex-like language, and a lot of structure associated with it.

I want to use the attach system, but I find the requirements of the package documentation onerous. They are meant to support packages for general release, but I am the only user here. I want a closer association between documentation and code, and a faster system of updating the HTML.

A system designed for this is Doxygen. This has an implementation as the R package roxygen2, based on embedded comments. This again works through .rd files which it turns into pdf. This is more cumbersome than I would like. I also want to use the system for objects other than functions, which can be included in the package.

So I have created a set of R programs which work through a list of lists, each specifying a function, or perhaps some other object that I would like to see in a package. I call that master list an object. It isn't useful in itself, but can easily be converted into a package that could be attached. But it can also easily generate the HTML documentation with appropriate layout. But a further facility is that it can generate a text file in which the documentation and code are listed together and can be edited, and then mapped back into the object form.

Although I adopted the scheme for its flexibility when I make frequent changes, I think it is now justified by the better active HTML reference page. Not only does it have various linked indexes, but you can choose to display the code next to the documentation. And since the code is placed there at the same time as it is entered into the package, it is guaranteed to be current, even if the manual editing of the description may lag.

The material for the object comes generally from collections of R functions used in a program environment, so there needs to be a scheme to import this into an object. It is desirable to be able to map back, because writing and debugging code is best done in the native form. Since the object contains a lot of text as well as functions, it is important to have a merge facility, so that updated functions can be imported while retaining the documentation.

This all works, and can be extended to other languages, although I'll refer to it as R code here.. I use it for Javascript. There isn't the exact equivalent of an attachable package, but code can still be output in a usable form. Importing from code works in much the same way, since it relies on functions being delimited by braces. So it could be used for C and similar languages as well. The main thing is that the html and text forms are useful in the same ways.

Here is a diagram of the various forms and transitions:


Pack
⇑↓
TextObjectCode
html


And here are more details about the entities. The naming convention will be that there is a stem name, and the files will be called stem.rda, stem.txt, stem.html The results can be accessed directly in the namespace, as well as returned.

Object

This is the basic entity. It is a list of lists, with two special entries at the head. Later names are those of functions. In each sublist:
$name function name
$def Just name with arguments
$txt Description, maybe with example results
$tag denotes a group like mesh, icos etc.
$code The function code, as it would appear in an R file
$fn An R function, derived from code

The two nonlists are
$name - the name of the object
$head - a nx2 matrix of strings. The first column are tags starting with "hQ", the second are text for corresponding headings.
The obj$name will be the name of the .rda file on which it is stored. However, on this file it will be called obj.

Naming in this system is consistent. You give a stem, and the object file and .txt, .html and .r that might be derived from it use that stem. So the stem is the input argument for many of the functions.

The idea is the object this can be created from (and can create) an editable txt file

txt

This is a character vector which also sits on a file. It contains all the information in obj, so either can be used to regenerate the entity. I keep the txt file of my main package open in an editor, and regenerate the obj file as required.
The txt file has the following appearance. The matter related to each function is enclosed in braces, so an editor like Notepad++ can collapse the text, showing just one heading per function. This is especially handy if you want to move stuff around. The headings are also enclosed in braces, and are significant in organising the material.

Within the braces for a function, there is a line, starting with wQ, followed by the name and tags
Following text will be the documentation
Then a line starting with wQ_ followed by the code of the function (comments will be maintained).

The text file can also have embedded lines starting with hQ_, then tags and headings. The tags will be attached to the objects that follow, and the headings will appear in the documentation.

So there are functions to convert
obj2txt(obj) makes the txt file
txt2obj(txt) makes the obj

Pack

The business end of the scheme is the pack. This is what is attached, and carries the functionality. It is what the user sees in the R environment. It is actually just a slightly rearranged version of the obj list, with the functions visible at top level, and the other material in a sub-list, so not visible from the workspace.

The associated function obj2pack(obj) puts n object into a list with package format. This puts the functions at the top level, so they can be attached, and puts a copy of the object in a list $packobj. It also renames the $name to $packname so it can sit at top level without being confused with other names. The reverse action is simply
obj<-pack br="" packobj.="">

HTML documentation

This is a major output. It is different to the previous, in that it cannot usefully be converted back to an object, so the only entity is an HTML file with the stem name. It is created by obj2html. Like most functions originating from obj, you can use tags to select a subset to document. In this post, the documentation shown was extracted from my comprehensive library by selecting a tag (hQdocu).

Source material and debugging


code2obj(file) takes an R code file with function definitions and pulls out the functions (with embedded comments). Functions within functions do not get a separate entry. It creates an obj which does not have any documentation attached, and only primitive tagging. So you either have to add this by then converting yo a txt and editing, or merge, using objmerge(new,old). The arguments are objects. In the result object, function lists with different names will simply be concatenated (new after old). Where names are the same, the function and its code will be from new. Headings then just merge, and for tag and txt it takes whichever is the longest - usually there is only one present.

The back operation is obj2r. I didn't call it obj2code, because while it creates working R code, I can't guarantee that for all languages, although it works for Javascript. The main use for this is debugging, which is hard to do in a package environment. Make an R file, debug or modify, then make a new object, and merge.

Conversions.


Covered above, but I'll summarise here. The chief conversion is from txt to object, and addition of object to package. Also need object back to txt.


So normal conversion steps are:
1. Generate an object from some (R) code (code2obj
2. Make a txt file (obj2txt)
3. Add documentation to the txt file (between the name and the function code), and headings as desired.
4. Convert back to object (txt2obj)
5. Make html file for checking, and for later documentation
6. Merge with another object using objmerge.

For serious debugging, make an r file with obj2code. Then, when it is right, you can merge the new file back. It will just replace the functions and code, but the documentation and headings and tags will be as before.

Example - the documentation system documented

Topic

Documentation

code2obj()
txt2obj()
obj2txt()
obj2obj()
obj2code()
obj2html()
objargs()
obj2pack()
html2obj()
objextract()
objmerge()

makepack()

Description

Documentation

A system for maintaining and documenting R code. A collection of R functions lives in a list called an object. There is a sublist included with each function, named by function name. The list includes the code and a parsed version, the name, tags and a descriptive string (txt). This central object can be transformed in various ways:.







Pack
⇑↓
TextObjectCode
html


The main transformation is to a text mode. This lists for each functin the code, descriptor and tags. This can be edited and transformed back; it is the place where documentation is added.

The transformation to html is how the documentation is viewed. There are tables of names wih links, and headed paras of description. There are buttons that will display code.

The ultimate aim is to use the functions as a package, that sits attached in the user space. Attached means that the functions are available as in a linked package.

The functions will normally be extracted from some R code, using code2obj. There is also the ability to put some set of functions back into R code, which is easier if there is a lot of debugging to do.

The functions which start with an object (obj2...) have a particular treatment of arguments. The first can be either an object in the workspace or a stem name. If the latter, it will load the object stem.rda. Then there is an optional argument tags. This can be a character vector, probably of tags. The program will then operate on the subset of functions that have one of those tags in their tag string, or in the name. So you can include functions by tag or name. This is useful for making sub-objects, or documenting portions, etc. If the first item in the tags vector is "!", then it excludes rather than includes the relevant functions. The heading is modified appropriately.
A note on naming and stems
Transfer functions generally allow you to provide the first argument as a entity in workspace or a stem. This is a string used for naming objects and files. The stem argument gets the input from a file made with that name, and generally outputs to a correponding file. So the files have consistent naming. Its best to do all work here in the same directory.

Objects sit on a stem.rda file, but their name there is just obj. But the name in the list will also be stem. Consistency matters here, but using default processes should ensure it. The name is first assigned with obj2obj (arg newname), and to get an object from file, obj2obj(stem) means you dont have to worry about what it is called.


  • code2obj(b="",stem="stem",tag="")

    The main mode of input to the system. Input can be a text string of function code (b). But if a stem is given (default is "stem"), and b="" (default), then it will read the file "stem.r". You can also give a single word for b, and that will be used for the input file name (with .r added if no suffix.
    The output is a list stem.rda, which is also returned, and can be used as an object. Remember, though, that it has no associated text information. You can either supply this with editing, via obj2txt, or merge with a file that already has metadata for functions of the same names (objmerge). In that case, the new function code with replace the old, but the metadata remains.
    You can use the system for other languages that define functions with code between braces. I use it for Javascript.
    You can assign tags using the tags argument (string). The useful default is to give the stem name of the originating file.

    function(b="",stem="stem",tag=""){ 
    # reads r code (as file or char vec) and makes obj
    if(b=="")b=pc(stem,".r")
    isjs=F
    if(length(b)<2){
    isjs=grepl(".js",b)
    if(!isjs & !grepl("\\.[rR]$",b))b=pc(b,".r")
    if(tag=="")tag=sub("\\..*","",b)
    b=readLines(b)
    }
    h=bracer(b)
    e=sub('#.*',"",b);
    e=sub('^[ ]*',"",e);
    if(isjs){
    k=grep("^function ",e)
    d=sub("^function ","",e[k]) # picks defs
    }else{
    e=gsub('<-',"=",e);
    k=grep("=function\\(",e)
    d=sub("=function","",e[k]) # picks defs
    }
    obj=list(name=stem,head=list()); ig=h$pairs
    q=rbind(k,k,k)
    o=ig[1,]%in%k # marks braces associated with functions
    i=match(ig[1,o],k)
    q[2,i]=ig[2,o] # marking range of fns
    q[3,]=cummax(q[2,])
    for(i in 1:ncol(q)){
    j=q[,i]
    if(j[2]<j[3])next # skip inner fns
    e=list()
    e$name=g=sub("\\(.*","",d[i])
    if(g=="box"){
    browser()
    }
    e$def=sub("\\).*","\\)",d[i])
    e$txt=""
    h=b[j[1]:j[2]]
    h[1]=sub(".*=function","function",h[1])
    if(g=="obj2obj")print(j)
    e$code=h
    e$fn=0
    if(!isjs)e$fn=eval(parse(text=h))
    e$tag=tag
    obj[[g]]=e;
    }
    save(obj,file=pc(stem,".rda"))
    invisible(obj)
    }

  • txt2obj(stem)

    Takes a "stem".txt file with comments and headings and creates an object, which it saves to "stem.rda". The headings and function lists are marked with special headings like hQ_ for heading, {wQ_, 1wQ_ and 2wQ} for function name, definition and end section, respectively. The heading structure gets built into the function tags.

    See obj2txt for more. But it is useful to know that when this function operates, it reorders as follows. The jQ tags are used in alphabetical order:
    1. The headings are first placed in order according to their jQ tags.
    2. The functions are placed below the heading determined by their hQ tag.
    3. They are then further ordered by their jQ tags.

    So if you want to reorder headings, change the jQ. To regroup a function with a different heading set, change the hQ tag to the right heading. You can use jQ to reorder within the group. Typically you find an entity, with tag say jQ121, that you want it to follow. So change the tag of the thing you want to move to, say, jQ121a.

    You can get rid of functions just by removing the section between braces. For doing several, obj2obj might be better.

    Be aware that this function, txt2obj, parses the R code and may generate errors.

    function(stem){ 
    first=function(d){
    i=regexpr(" ",paste(d," "))
    drop(cbind(substr(d,1,i-1),substr(d,i+1,99)))
    }
    txt=function(b,i,j){ # get txt between limits
    s=""; k=j-i-1; if(k>0)s=b[i+1:k]
    s
    }
    sp=paste(stem,c("rda","txt","html"),sep=".")
    b=readLines(sp[2])
    jb=grep("^.[hw]Q",b)
    j=t(matrix(sort(c(jb,grep("^1hQ",b))),nrow=3))
    o=j[,2]==j[,3]; k=cumsum(o); # o is headings
    bj=sub("wQ__*","",substr(b[j[,1]],2,99))
    ob=!grepl("hQ",bj)
    bw=getword(bj,2)
    bw[o,3]=bw[o,2]; bw[o,2]="";
    bw[ob,2]=bw[o,1][k[ob]] # fills missing hQ
    #bw[,2]=bw[o,2][k] # replace with hQ removed
    ik=1:sum(o)
    ik[order(bw[o,3])]=ik; # jQ tag order
    k=ik[k]
    k[o]=k[o]-0.1 # make sure header goes first
    ko=order(k,bw[,3]) # order by header, then body
    bw=bw[ko,]; jk=j[ko,]; ok=o[ko];
    nb=nrow(bw)
    row.names(bw)=NULL
    tags=paste(bw[,2]," jQ",(1:nb+99)," ",sub("jQ[^ ]*"," ",bw[,3]),sep="")
    tags[ok]=bw[ok,3]
    #bw[ok,1]=bw[ok,2]

    obj=list(name=stem,head=list())
    for(i in 1:nrow(bw)){
    j=jk[i,]; n=bw[i,1]
    p=list(name=n,tag=tags[i],txt=txt(b,j[1],j[2]))
    if(!ok[i]){
    v=txt(b,j[2]-1,j[3])
    if(grepl("function",v[1])){
    s=".*function"
    p$def=sub("\\).*","\\)",sub(s,n,v[1]))
    v[1]=sub(s,"function",v[1])
    p$code=v
    p$fn=eval(parse(text=v))
    }
    obj[[n]]=p
    }else{obj$head[[n]]=p}
    }
    save(obj,file=sp[1])
    invisible(obj)
    }

  • obj2txt(stem,tags=NULL)

    Generates a txt file of proper format with headings, names, code, txt for documentation etc. This can be edited and turned back into an object.

    Editing the text file is the usual way of entering documentation and headings. The format for headings, say for mesh, is:
    {hQ_mesh jQ207
    text goes here
    1hQ}

    and for functions is
    {wQ__________objargs hQ_docu jQ231 Moymakedoc
    text goes here
    1wQ_function(){
    function code goes here
    }
    2wQ}

    Most of this (except text) is created by obj2txt. The headings are not, but there is usually a prototype at the end of file called hQ_NULL. You replace NULL with your own name, put it in the file where you want it, and write some text. The top line will be used as heading.

    The hQ and jQ tags are used for ordering things; the third tag is c reated from the file name of the originating code. You can write further tags as wanted. Ill explain the hQ/jQ system in txt2obj.

    Note the braces; in an editor that supports it, you can collapse the text so that only the top line of each section is visible.

    The txt file contains all the information used to make an object with txt2obj.

    function(stem,tags=NULL){ 
    obj=objargs(stem,tags)
    b=NULL; if(!is.list(obj$head))browser() #obj=rehead(obj)
    ed=obj$head
    ht=listtags(obj)
    print(pc(nrow(ht)+length(ed)," items went in"))
    tg=ht[,2]
    hg=cmatch(tg,names(ed))
    o=is.na(hg); # has no matching hQ
    oi=grepl("hQ",tg) & o # has hQ but no match
    if(sum(o)>0){# gives hQ tag to those without
    tg[oi]=gsub("hQ[^ ]*","",tg[oi])
    hg[o]=hh="hQ_NULL"
    ed[[hh]]=list(name=hh,txt="Add text here")
    tg[o]=paste(hh,tg[o])
    }
    j=100;
    for(h in ed){ # loop over heads
    hn=h$name
    b=c(b,pc("{",hn," jQ",j),h$txt,"1hQ}"); j=j+1
    ik=which(hg==hn)
    for(ig in ik){ # loop over functions
    g=ht[ig,1]
    k=obj[[g]] # noting they should all be caught by hQ
    f=gsub("jQ[^ ]*"," ",tg[ig])
    f=getword(f,"hQ");
    p=pc(f[2]," jQ",j," ",f[3]," ",f[1]) # new tag
    j=j+1
    f=k$code; if(is.null(f))next
    f[1]=pc("1wQ_",f[1])
    b=c(b,pc("{wQ__________",k$name," ",p),k$txt,f,"2wQ}")
    }
    }
    print(pc(j-100," items stored"))
    write(b,file=s[2])
    } # end obj2txt

  • obj2obj(stem,tags=NULL,newname="")

    This takes an object as arg, and retuens it. It is useful for side-effects:
    1. If the first arg is a stem, the program will read the obj from file and return it
    2. As explained in the heading section, you can use the tags arg to select items for inclusion
    3. Setting the newname arg to a string with give the object that name, and store it on name.rda.

    function(stem,tags=NULL,newname=""){ # reads from rda and saves 
    obj=objargs(stem,tags)
    if(newname!=""){
    obj$name=newname
    save(obj,file=pc(newname,".rda"))
    }
    invisible(obj)
    } # end obj2txt

  • obj2code(stem, tags=NULL)

    Generates code from the object. Useful when a lot of debugging is needed. The follow-up is code2obj, followed by merge. The code does not have any documentary info, but that should be restored on merging, providing names are not changed.

    The tags arg again allows selection. You can output code other than r, and it will probably work, but I can't guarantee that the format will resulting in working code. It works for JavaScript. Regardless of language, the text outpute will be suffixed .r.

    function(stem, tags=NULL){ 
    obj=objargs(stem,tags)
    b=NULL
    for(i in obj){
    if(!is.list(i))next;
    u=i$code
    u[1]=pc(i$name,"=",u[1])
    b=c(b,u)
    }
    write(b,file=s[4])
    }

  • obj2html(stem,tags=NULL)

    Creates the HTML documentation. There is a table of links by topic, then the main listing, then a table of alphabetical links. The main list includes buttons to toggle display of code. Headings are used here. If tags are listed, the output will be restricted to items with those tags.

    function(stem,tags=NULL){ 
    # makes a html file with links
    obj=objargs(stem,tags)
    if(length(obj)<2){print(pc(stem," did not find valid object")); return(0);}
    is='<td style="width:140px;vertical-align:top">'
    it=pc(is,'<b style="color:#aa0000">')
    now=""
    z=c('<html>
    <meta charset="UTF-8">',
    "<h4>Topic</h4> <table> <tr>")
    z2=cbind(" ",pc("<h4>Alphabetical</h4> <table> <tr>",is))
    z1="</table><h4>Description</h4><ul>"
    # z,z1 and z2 are chunks of html to make file
    tg=listtags(obj)
    ic=ik=0
    for(hf in obj$head){
    he=hf$name; ht=hf$txt
    zp=pc('<h4 style="color:#aa0000" id="',he,'">',ht[1],"</h4>")
    if(ic>4){z=c(z,"<tr>");ic=0;}
    z1=c(z1,zp,'<span style="color:#770000">',paste(ht[-1],"<br>"),"</span>"); ic=ic+1;ik=0
    z=c(z,pc(it,ht[1],"</b><br>"))
    j=grep(he,tg[,2])
    for(i in tg[j,1]){
    ob=obj[[i]]
    ze=paste('<a href="#',i,'">',i,"()</a><br>",sep="")
    z=c(z,ze);
    ik=ik+1;if(ik>10){z=c(z,is,"<br>");ik=0;ic=ic+1;}
    z2=rbind(z2,c(ob$name,pc(ze)))
    z1=c(z1,pc('<br id="',i,'"><li>',ob$def,"<br>"),'<span style="color:#004488">',paste(ob$txt,"<br>"),"</span>");
    }

    }

    i=order(z2[,1]);
    z2=insert(is,z2[i,2],seq(20,1200,20))
    a=c(z,z1,z2,"</table>")
    write(a,file=s[3])
    a
    }

  • objargs(stem,tags=NULL)

    Mainly used internally. The first argument "stem" can be either an object or a stem name. In either case it will put into the workspace (which is usually that of the function)
    1. obj, which is either the object of the argument, or read from stem.rda, if stem is a string
    2. s - a vector of filenames with stem and various suffices. If the arg was an object, the stem is the object name
    3. os is a logical saying whether "stem" was an object or not.
    The other important function is to allow components of the object to be selected by tag. The object generated by the stem argument will be filtered so that only components with at least one of the listed tags are included. Useful with obj2obj

    function(stem,tags=NULL){ 
    # Gets 2 args of obj2 functions and preprocesses
    os=length(stem)>1
    if(os){obj=stem; stem=obj$name}
    s=paste(stem,c("rda","txt","html","r"),sep=".")
    if(!os){
    a=load(s[1]);
    if(a!="obj")assign("obj",eval(parse(text=a)))
    }
    if(length(tags)>0){ # selects subset by tags
    g=listtags(obj)
    og=tags[1]=="!"; if(og)tags=tags[-1]
    cg=paste(g[,1],g[,2])
    o=!is.na(cmatch(cg,tags))
    if(og){o=!o; o[1:2]=F;
    gw=getword(g[,2],"hQ")
    tags=grep("hQ",unique(gw[o]),val=T)
    }
    ig=c(1:2,which(o))
    obj=obj[ig]
    obj$name=pc(obj$name,"_",tags[1])
    oh=obj$head
    o=tags %in% names(oh)
    if(sum(o)==0)stop("No tags could be matched.")
    obj$head=obj$head[tags[o]]
    }
    s<<-s;
    invisible(obj)
    }

  • obj2pack(obj,stem,tags=NULL)

    Reformats an object into a list (pack) in which the object is just an element, called packobj, and the name is referenced as "packname". A copy of each function, listed by its name, is placed at the top level. This enables attachment, so that only the reserved words packobj and packname and the actual function names will be visible in the search path.

    function(obj,stem,tags=NULL){ 
    obj=objargs(obj,tags)
    if(grepl("JS",obj$name)){
    s=NULL
    for(x in obj){
    if("code" %in% names(x)) s=c(s,paste(x$code,"\\",sep=""))
    }
    s[1]=pc("MoyhuJSlib= '",s[1])
    i=length(s)
    s[i]=sub("\\\\","'",(s[i]))
    write(s,file=pc(stem,".js"))
    return()
    }
    pack=list(packname=stem,packobj=obj)
    np=pack$packname
    for(i in obj){
    if(!is.list(i))next
    n=i$name
    if(is.null(n))next
    f=i$fn
    if(grepl("Rdata",i$tag)){
    if(!exists("packdata"))load("Rdata.rda")
    f=packdata[[n]]
    }
    if(!is.null(f)){
    pack[[n]]=f
    }else{print(pc("No function or data found for ",n))}
    }
    #assign(np,pack)
    s=pc(stem,".rda")
    print(pc("Saving pack to ",s))
    save(pack,file=s)
    invisible(pack)
    }

  • html2obj(f)

    An obsolete routine originally developed to allow documentation to be recovered from a html file. Shouldnt now be needed.

    function(f){ 
    b=readLines(f)
    j=grep("id=",b)
    h=sub('.*id="',"",b[j])
    names=sub('">.*',"",h)
    defs=sub("<br>","",sub('.*li>',"",b[j]))
    j1=j[1]-1
    M=list()
    for(i in 2:length(j)){
    h=names[i-1]
    s=list(name=h,def=defs[i-1],txt="",code="",tag="mesh")
    j0=j1+2
    j1=j[i]-1
    if(j1>=j0)s$txt=b[j0:j1]
    M[[h]]=s
    }
    M[["names"]]=names
    } #end html2obj

  • objextract(obj)

    Allows you to put elements of an object in the workspace. Of limited use.

    function(obj){ 
    for(i in obj){
    if(length(i)!=5)next
    assign(pc(i$name),i$fn)
    }
    } # end objextract

  • objmerge(obj,stem,rule="")

    Important. Takes a new, usually smaller, object and merges it with "old". The rules of the merge are:
    1. Elements from new that arent in old will be inserted, proably at the end.
    2. For elements that are in both, a single element will remain, which has the code and function defined in new (if there is one, else old). The text and tags will be chosen according to which choice has more characters (usually one is empty). It tries to merge headings rationally.
    This is how big objects are built.

    function(obj,stem,rule=""){ # folds obj into old 
    best=function(a,b){x=a;if(sum(nchar(a))<sum(nchar(b))){x=b}; x;} # keeps longest
    old=objargs(stem)
    c1=c("txt","tag","fn","code","def");c2=c(2,2,1,1,1);
    for(i in 1:5)if(grepl(c("t","T","g","G","f")[i],rule))c2[i]=(i%%2)
    c2[4:5]=c2[3]
    merge=function(a,b){
    if(is.null(a))return(b)
    na=names(a);nb=names(b);
    for(i in na){
    if(i =="head")next
    A=a[[i]]
    if(!is.list(A))next
    m=match(i,nb)
    if(!is.na(m)){
    B=b[[m]]
    for(j in 1:5){
    u=c1[j]
    h=A[[u]]; n=c2[j]
    if(is.null(h))next
    if(n==1 | is.null(B[[u]])) {B[[u]]=h}
    else{B[[u]]=best(h,B[[u]])}
    }
    b[[m]]=B
    }else{b[[i]]=A}
    }
    b
    }
    old$head=merge(obj$head,old$head)
    old=merge(obj,old)
    print(old$Max$tag)
    save(old,file=s[1])
    invisible(old)
    }

  • makepack(obj,stem)

    A wrapper for obj2pack. It reformats it, puts it in the workspace with the right name, and saves it as .rda file. But it doesnt attach it.

    function(obj,stem){ 
    pack=obj2pack(obj,stem)
    save(pack,file=pc(stem,".rda"))
    invisible(pack)
    }

    Alphabetical

    code2obj()
    html2obj()
    makepack()
    obj2code()
    obj2html()
    obj2obj()
    obj2pack()
    obj2txt()
    objargs()
    objextract()
    objmerge()
    txt2obj()

    Zipfile of code and entities

    I have put a zipfile here which contains the various entities referred to in this post. There are docdoc.rda, docdoc.html, docdoc.txt, docdoc.r and docpack.rda. They were made from my comprehensice obj Latest_all.rda with the following commands:
    fp="docdoc"
    obj2obj("Latest_all",tags="hQ_docu",new=fp)
    obj2html(fp)
    obj2txt(fp)
    makepack(fp,stem="docupack")
    obj2code(fp)
    

  • 3 comments:

    1. Have you looked at rmarkdown,rnotebooks, and rblogdown

      ReplyDelete
      Replies
      1. Steven,
        Yes, but I always learn more when you comment. However, I think they are more concerned about producing good looking reports with embedded functionality. For me, the business end is the package, which I use all the time. So I am seeking compromises which will allow me to:
        1. Keep the documentation up to date. That means automatically including he code as currently used, and to have the doc handy and writeable, close to that code
        2. To be able to easily shift the package code into and back from a R console environment for debugging or enhancement, while preserving the metadata.

        Consistency has been a big issue - to make sure there aren't clashing versions. That is why I run everything through the central object.

        I probably should use rblogdown for the monthly temperature reports.

        Delete
      2. Oh thanks.

        I used to do packages as well, but the documentation always was a pain.
        I believe at some point I had a system fo doing a bare bones doc file
        so it was quick to build because the docs were basically just stubs.

        defeated the purpose of documentation, but made building a package easier.

        When I get time I will have a look at your code.

        Delete