#----------------------------------------------------------------------- # Função para exibir diretórios em árvore ascii. connectors <- function(x) { # Função que retorna em "con" os conectores para cada nó, colocados # na frente dos nomes de arquivos ou diretórios, sendo "`--" para os # nós final e "|--" para os nós intermediários. Em "pre" são # retornados aquilo que deve ser prefixo dos conectores para os # descendentes do nó caso ele seja um diretório, sendo " " para nó # final e "| " para nós intermediários. tms <- c(length(x) - 1, 1) list(con = rep(c("|-- ", "`-- "), tms), pre = rep(c("| ", " "), tms)) } path2bigNestedList <- function(path, type = "none", all.files = TRUE) { # Tipos de ordenamento em que "none" é o que vem do list.files() e # "dir_last" é para ordenar deixando os diretórios para o final. type <- match.arg(type, choices = c("none", "dir_last")) # Se all.files == TRUE então diretórios é arquivos ocultos serão # exibidos. all.files <- all.files[1] # path: é um caminho de diretório. if (length(path) == 1) { # pre: o que antecede o contector para apropriada indentação e # conexão dos arquivos. # conn: conector que antecede o nome do arquivo/diretório. # path: caminho do arquivo/diretório. # child: o que é passado para dos descentes desse nó, caso ele # seja um diretório, como "pre" para haver apropriada indentação # e conexão entre arquivos/diretórios. path <- c(pre = "", conn = "", path = path, child = "") } # É diretório? isdir <- isTRUE(file.info(path["path"])$isdir) # Tem conteúdo? isnempty <- (length(dir(path["path"])) >= 1) # Só entra no if diretórios não vazios. if (isdir && isnempty) { # Retina a possível barra no do nome de diretórios para previnir # acidentes. path["path"] <- sub(x = path["path"], pattern = "/$", replacement = "") # Cria o texto do nó = pre + conn + path. path["text"] <- paste(basename(path[1:3]), collapse = "") # Lista arquivos e diretórios dentro do path informado. lf <- list.files(path["path"], all.files = all.files, no.. = TRUE, include.dirs = TRUE, full.names = FALSE) if (type == "dir_last") { lf <- lf[order(file.info( paste(path["path"], lf, sep = "/"))$isdir)] } # Diretórios vazios não retornarão nada do list.files, então # não correr parte do código. if (length(lf) >= 1) { # Obtém os conectores e prefixos para cada # arquivo/diretório. aux <- connectors(lf) # Cria matriz com os elementos necessários, em cada linha um # arquivo/diretório, e nas colunas os elementos do nó. paths <- cbind(pre = paste(path["child"], collapse = ""), conn = aux$con, path = paste(path["path"], lf, sep = "/"), child = paste(path["child"], aux$pre, sep = "")) # No caso de ter apenas uma linha, que dá problema com # apply() que precisa de nrow > 1, trabalha como vetor para # obter o texto do nó = pre + conn + path. if (nrow(paths) == 1) { u <- paste(paths[1, 1:3], collapse = "") } else { u <- apply(paths[, 1:3], MARGIN = 1, FUN = function(x) { paste(basename(x), collapse = "") }) } # Transforma o que eram linhas em elementos de lista. paths <- split(cbind(paths, u), 1:nrow(paths)) # Garante os nomes corretos. paths <- lapply(paths, "names<-", names(path)) # Chamada recursiva com os mesmo argumentos. lf <- lapply(paths, FUN = path2bigNestedList, type = type, all.files = all.files) } # Adiciona o path inicial. lf <- c(sprintf("%s/", path["text"]), lf) } else { # Quando não existirem mais diretórios, retorna só o texto do # nó. A barra no final sinaliza os diretórios. lf <- sprintf("%s%s", path["text"], ifelse(isdir, yes = "/", no = "")) } return(lf) } #' @name dirtree #' @export #' @author Walmes Zeviani, \email{walmes@@ufpr.br}. #' @title Exibe o Diretórios como Árvores ASCII #' @description Essa função é uma implementação em R do comando #' \href{http://mama.indstate.edu/users/ice/tree/}{\code{tree}} no #' Linux. Essa função exibe diretórios em estrutura de árvore em #' modo texto. #' @param path Uma string de tamanho 1 que é caminho a partir do qual #' desenhar as ramificações. #' @param type Uma string de tamanho 1 que é o critério de exibição. O #' default e \code{"dir_last"} que deixa para exibir diretórios no #' final da lista e arquivos no topo. Ordem alfabética padrão é #' retornada com a opção \code{"none"}. #' @param all.files Um valor lógico de tamanho 1 onde \code{TRUE} #' significa exibir arquivos e diretórios ocultos. #' @return Não retorna conteúdo, apenas exibe no console a estrutura do #' diretório. #' @seealso \code{\link[base]{dir}}, \code{\link[base]{list.files}}. #' @examples #' #' \donttest{ #' #' # Tree of your currend directory. #' getwd() #' dirtree() #' dirtree(type = "none") #' dirtree(all.files = TRUE) #' #' # Tree of home folder. #' dirtree("~/") #' #' # Tree of a installed package. #' dirtree(system.file(package = "lattice")) #' #' } dirtree <- function(path = "./", type = "dir_last", all.files = FALSE) { cat(unlist(path2bigNestedList(path = path, type = type, all.files = all.files), recursive = TRUE), sep = "\n") } #----------------------------------------------------------------------- # Verifica se existe a tetra de aquivos txt, rda, R e Rd. #' @name check4files #' @title Verifica existência dos 4 Arquivos Associados de um Dataset #' @description Verifica os 4 arquivos associados de um dataset: txt, #' rda, R e Rd. #' @param x Um vetor com nome de um objetos. #' @return Uma matriz de valores lógicos. #' @author Walmes Zeviani, \email{walmes@@ufpr.br}. #' @examples #' #' t(sapply(ls("package:labestData"), FUN = check4files)) #' check4files <- function(x) { c(txt = file.exists(sprintf("./data-raw/%s.txt", x)), rda = file.exists(sprintf("./data/%s.rda", x)), R = file.exists(sprintf("./R/%s.R", x)), Rd = file.exists(sprintf("./man/%s.Rd", x))) } #----------------------------------------------------------------------- # Gera código para tabelas dentro da documentação roxygen2. # http://r-pkgs.had.co.nz/man.html#man-special tabular <- function(df, ...) { stopifnot(is.data.frame(df)) align <- function(x) { if (is.numeric(x)) { "r" } else { "l" } } col_align <- vapply(df, align, character(1)) cols <- lapply(df, format, ...) contents <- do.call("paste", c(cols, list(sep = " \\tab ", collapse = "\\cr\n "))) paste("\\tabular{", paste(col_align, collapse = ""), "}{\n ", contents, "\n}\n", sep = "") } #----------------------------------------------------------------------- # Funções para gerar esqueleto de documentação. # Função que verifica onde e com que nome criar o arquivo com o # esqueleto da documentação. where_save <- function(name, file, append = FALSE) { # Faz arquivo ter o nome do objeto. if (missing(file)) { file <- sprintf("%s.R", name) } # Onde salvar. if (!is.na(file)) { if (file.exists("DESCRIPTION")) { file <- sprintf("R/%s", file) } if (!append & file.exists(file)) { stop(sprintf(paste("File `%s` already exists.", "Use append = TRUE or remove it."), file)) } } return(c(file = file)) } # where_save("fun") # where_save("fun", file = "as.R") # where_save("fun", file = "func.R", append = FALSE) # where_save("fun", file = "func.R", append = TRUE) #' @name roxy_fun #' @export #' @title Gera o Esqueleto de Documentação de Funções em \code{roxygen2} #' @description Função que recebe uma função e produz o esquelo da #' documentação da função em \code{roxygen2}. Opções dessa função #' permitem escrever o esqueleto em arquivo, adicionar campos e #' abrir o arquivo gerado com algum editor de texto. #' @param object Um objeto que é uma função. #' @param file Um nome de arquivo onde escrever a documentação e a #' definição da função. Quando não fornecido, o nome da função é #' usado como nome do arquivo. Se for usado \code{NA}, nenhum #' arquivo será criado e a documentação será exibida no console. #' @param export Valor lógico que indica se deve escrever \code{@export} #' ou não para esta função. #' @param author O autor da função. Este conteúdo será passado para o #' campo \code{@author}. #' @param keywords Um vetor com keywords para a função que serão #' escritas no campo \code{@keywords}. #' @param extra Vetor com o conteúdo de campos extras como #' \code{"@import lattice"}. #' @param editor Nome do editor com o qual abrir o arquivo para fazer o #' preenchimento dos campos da documentação. Veja #' \code{\link[utils]{edit}}. #' @param print Valor lógico que indica se deve imprimir no console o #' esqueleto de documentação gerado. #' @param append Valor lógico que indica se deve escrever a documentação #' em arquivo que já existe. #' @param find_file Valor lógico que indica se deve exibir no console o #' caminho do arquivo gerado com a documentação. #' @return Essa função não retorna conteúdo mas cria/modifica arquivos. #' @examples #' #' fun <- function(x, y, ...) { #' return(x + y) #' } #' #' file.remove("bla.R") #' file.remove("fun.R") #' #' roxy_fun(fun) #' roxy_fun(fun, append = TRUE) #' #' file.remove("fun.R") #' roxy_fun(fun, find_file = TRUE) #' #' file.remove("fun.R") #' roxy_fun(fun, editor = "emacs") #' #' roxy_fun(fun, file = NA) #' #' file.remove("fun.R") #' roxy_fun(fun, print = TRUE) #' #' file.remove("fun.R") #' roxy_fun(fun, export = FALSE) #' #' file.remove("fun.R") #' roxy_fun(fun, author = "Walmes Zeviani, \\email{walmes@@ufpr.br}.") #' #' roxy_fun(fun, file = "bla.R") #' #' file.remove("bla.R") #' roxy_fun(fun, file = "bla.R", extra = "@import lattice") #' #' file.remove("fun.R") #' roxy_fun(object = fun, editor = "emacs") #' roxy_fun <- function(object, file, export = TRUE, author, keywords, extra, editor, print = FALSE, append = FALSE, find_file = FALSE) { # Nome da função. name <- deparse(substitute(object)) file <- where_save(name, file, append) # Argumentos da função. params <- names(formals(object)) # Conteúdo da documentação. ctnt <- c(sprintf("@name %s", name), if (!missing(author)) { sprintf("@author %s", author) }, if (export) { "@export" }, "@title", "@description", sprintf("@param %s", params), "@return", if (!missing(keywords)) { paste(c("@keywords", keywords), collapse = " ") }, if (!missing(extra)) { extra }, "@examples") ctnt <- paste("#\'", ctnt) if (is.na(file)) { cat(ctnt, sep = "\n") } else { # Exporta a documentação para o arquivo. cat(ctnt, sep = "\n", file = file, append = append) if (print) { cat(ctnt, sep = "\n") } # Exporta a função para o arquivo. dump(name, file = file, append = TRUE) # Abre arquivo no editor. if (!missing(editor)) { editor <- match.arg(arg = editor, choices = c("vi", "emacs", "pico", "xemacs", "xedit")) do.call(editor, list(file = file)) } } if (find_file) { cat(sprintf("(find-file \"%s\")", paste(path.expand(getwd()), file, sep = "/")), "\n") } invisible() } #' @name roxy_data #' @export #' @title Gera o Esqueleto de Documentação de Datasets em #' \code{roxygen2} #' @description Função que recebe uma conjunto de dados e produz o #' esquelo da documentação em \code{roxygen2}. Opções dessa função #' permitem escrever o esqueleto em arquivo, adicionar campos e #' abrir o arquivo gerado com algum editor de texto. #' @param object Um objeto que armazena dados. Classes compreendidas são #' \code{data.frame} e vetores. #' @param file Um nome de arquivo onde escrever a documentação do #' conjunto de dados. Quando não fornecido, o nome do objeto é usado #' como nome do arquivo. Se for usado \code{NA}, nenhum arquivo será #' criado e a documentação será exibida no console. #' @param source String que é a referência bibliográfica do conjunto de #' dados. #' @param keywords Um vetor com keywords para o conjunto de dados que #' serão escritas no campo \code{@keywords}. #' @param author O autor do conjunto de dados. Este conteúdo será #' passado para o campo \code{@author}. #' @param extra Vetor com o conteúdo de campos extras como #' \code{"@import lattice"}. #' @param editor Nome do editor com o qual abrir o arquivo para fazer o #' preenchimento dos campos da documentação. Veja #' \code{\link[utils]{edit}}. #' @param print Valor lógico que indica se deve imprimir no console o #' esqueleto de documentação gerado. #' @param append Valor lógico que indica se deve escrever a documentação #' em arquivo que já existe. #' @param find_file Valor lógico que indica se deve exibir no console o #' caminho do arquivo gerado com a documentação. #' @return Essa função não retorna conteúdo mas cria/modifica arquivos. #' @examples #' #' s <- "Smith; Sanders (1234)" #' file.remove("iris.R") #' roxy_data(iris, #' print = TRUE, #' source = s, #' editor = "emacs", #' keywords = c("BLA", "BLU"), #' find_file = TRUE, #' extra = c("@docType dataset", #' "@details bla bla bla")) #' roxy_data <- function(object, file, source = NULL, keywords, author, extra, editor, print = FALSE, append = FALSE, find_file = FALSE) { # Nome do objeto. name <- deparse(substitute(object)) file <- where_save(name, file, append) # Determina a classe. cld <- class(object)[1] # Esqueleto do @format para cada classe. frmat <- switch(cld, "data.frame" = { f <- sprintf(paste( "@format Um \\code{data.frame} com %d", "observações e %d variáveis, em que"), nrow(object), ncol(object)) f <- strwrap(f, width = 69) f <- c(f[1], paste(" ", f[-1])) c(f, "", "\\describe{", "", rbind(sprintf("\\item{\\code{%s}}{ }", names(object)), ""), "}") }, "numeric" = { sprintf( "@format Um vetor com %d elementos.", length(object)) }, stop(paste("`object` de classe não apropriada", "para a função."))) # Conteúdo da documentação. ctnt <- c(sprintf("@name %s", name), if (!missing(author)) { sprintf("@author %s", author) }, "@title", "@description", frmat, if (!missing(extra)) { extra }, if (!missing(keywords)) { paste(c("@keywords", keywords), collapse = " ") }, if (!is.null(source)) { s <- strwrap(paste("@source", source), width = 69) c(s[1], paste(" ", s[-1])) }, "@examples") # Evitar espaço no final de linhas vazias. ctnt <- c(paste(ifelse(ctnt == "", "#\'", "#\' "), ctnt, sep = ""), "NULL") if (is.na(file)) { cat(ctnt, sep = "\n") } else { # Exporta a documentação para o arquivo. cat(ctnt, sep = "\n", file = file, append = append) if (print) { cat(ctnt, sep = "\n") } # Abre arquivo no editor. if (!missing(editor)) { editor <- match.arg(arg = editor, choices = c("vi", "emacs", "pico", "xemacs", "xedit")) do.call(editor, list(file = file)) } } if (find_file) { cat(sprintf("(find-file \"%s\")", paste(path.expand(getwd()), file, sep = "/")), "\n") } invisible() } #----------------------------------------------------------------------- #' @name write2txt #' @title Cria Arquivo Texto (tsv) do Conjunto de Dados #' @description TODO #' @param dataset Um objeto que seja vetor ou \code{data.frame}. #' @param overwrite Se TRUE, reescreve o arquivo se ele existir. #' @return Não retorna nada, apenas cria arquivos. #' @author Walmes Zeviani, \email{walmes@@ufpr.br}. #' @examples #' #' write2txt(iris) #' write2txt(precip) #' write2txt <- function(dataset, overwrite = FALSE) { if (!file.exists("DESCRIPTION")) { stop(paste0("Arquivo DESCRIPTION não encontrado. ", "Você não está na raíz de um pacote R. ", "Você está em ", getwd(), ".")) } name <- deparse(substitute(dataset)) cld <- class(dataset)[1] whatis <- cld == c("data.frame", "numeric", "integer") if (all(!whatis)) { stop("`dataset` de classe não reconhecida pela função.") } file <- sprintf("./data-raw/%s.txt", name) if (!file.exists(file) || overwrite) { cat(sprintf("Criando txt do dataset %s em data-raw/.", file), "Não edite na mão esse arquivo.", sep = "\n") switch(c(1, 2, 2)[whatis], "1" = { write.table(x = dataset, file = file, quote = FALSE, row.names = FALSE, sep = "\t", fileEncoding = "utf-8") }, "2" = { writeLines(text = as.character(dataset), con = file) }) } } #' @name write2rda #' @title Cria Arquivo rda do Conjunto de Dados #' @description Nada mais é que uma função wraper para a #' \code{devtools::use_data()}. #' @param dataset Um objeto que seja vetor ou \code{data.frame}. #' @param overwrite Se TRUE, reescreve o arquivo se ele existir. #' @return Não retorna nada, apenas cria arquivos. #' @author Walmes Zeviani, \email{walmes@@ufpr.br}. #' @examples #' #' write2rda(iris) #' write2rda(precip) #' write2rda <- function(dataset, overwrite = FALSE) { if (!file.exists("DESCRIPTION")) { stop(paste0("Arquivo DESCRIPTION não encontrado. ", "Você não está na raíz na raíz de um pacote R. ", "Você está em ", getwd(), ".")) } cmd <- sprintf("use_data(%s, overwrite = %d)", deparse(substitute(dataset)), overwrite) eval(parse(text = cmd)) } #-----------------------------------------------------------------------