Giter VIP home page Giter VIP logo

Comments (16)

smbache avatar smbache commented on August 25, 2024

It's possible, although it is a little tricky (maybe there's a better way).
BTW, what is dplyr calling on after a last pipe?

Anyway, here's a small boiled down example, maybe it can be prettier.

# Check whether inside a pipeline.
is_piped <- function()
{
  parents <- lapply(sys.frames(), parent.env)

  is_magrittr_env <-
    vapply(parents, identical, logical(1), y = environment(`%>%`))

  answer <- any(is_magrittr_env)

  list(answer = answer, 
       env    = if (answer) sys.frames()[[min(which(is_magrittr_env))]])  
}


# This is just a jq placeholder.
# Here just squares the number
jq <- function(x) 
{
  print("jq Called!")

  x^2
}

# Utility for hacking the effect into the pipeline
activate_jq_for_result <- function(env)
{
  res <- NULL

  jq_result <- function(v) 
  { 
    if (missing(v)) {
      res
    }
    else {
      res <<- v
      res$value <<- jq(res$value) 
    }
  }

  makeActiveBinding("result", jq_result, env)
}

# If this function is used in a pipeline it will end up calling jq!
# other than activating jq, it is just an identity function.
f <- function(x)
{

  piped <- is_piped()
  if (isTRUE(piped$answer) && 
      !exists('.jq_activated', envir = piped$env, inherits = FALSE)) {

    piped$env$.jq_activated <- TRUE
    activate_jq_for_result(piped$env)
  }

  x
}



10 %>% f %>% f %>% f
#> "jq Called!"
#> 100

f(f(f(10)))
#> 10

from jqr.

smbache avatar smbache commented on August 25, 2024

PS: This is activated if f is anywhere in the pipeline, and only if it has not already been done.
It will be called on the final result, before returning from the pipe.

from jqr.

sckott avatar sckott commented on August 25, 2024

@smbache thanks very much, this looks like exactly what I need!

BTW, what is dplyr calling on after a last pipe?

I don't know. @hadley how do you know to execute commands in dplyr after the last pipe?

from jqr.

smbache avatar smbache commented on August 25, 2024

no worries! BTW seems like a cool pkg, this!

from jqr.

hadley avatar hadley commented on August 25, 2024

It's in the print method in dplyr, but it doesn't run the whole query - it just runs enough to retrieve n rows. You have to use an explicit collect() to get everything

from jqr.

smbache avatar smbache commented on August 25, 2024

@sckott let me know if the above is useful then, and if you need me to elaborate on what's going on. The names should help a bit, but they can do only so much...

from jqr.

sckott avatar sckott commented on August 25, 2024

Thanks @hadley !

@smbache will do, will try implementing soon

from jqr.

sckott avatar sckott commented on August 25, 2024

@smbache works great - implemented on dsl-pipe-fix, devtools::install_github("ropensci/jqr", ref="dsl-pipe-fix")

e.g.,

'[0, false, [], {}, null, "hello"]' %>% types
#> ["number","boolean","array","object","null","string"]
'{"foo": 5, "bar": 7}' %>% keys
#> ["bar","foo"]
'{"foo": 5, "bar": 7}' %>% select(a = .foo)
#> {"a":5}

no need for the jq() call at the end now :)

from jqr.

karthik avatar karthik commented on August 25, 2024

This is super cool! 💯

from jqr.

sckott avatar sckott commented on August 25, 2024

thx - almost to a CRAN 1st version

from jqr.

smbache avatar smbache commented on August 25, 2024

Great!! :)

from jqr.

richfitz avatar richfitz commented on August 25, 2024

Thanks so much @smbache and @hadley

from jqr.

smbache avatar smbache commented on August 25, 2024

Wondering if you need some of the functions to switch off the mechanism? Say peek...

from jqr.

sckott avatar sckott commented on August 25, 2024

@smbache Yes, for peek, combine, and possibly others, Any ideas?

from jqr.

smbache avatar smbache commented on August 25, 2024

Sure. I have modified my original example to go both ways. I included example documentation, which you can use if you feel like it. I could have made a PR, but I'll let you do the dirty work ;-) Let me know if this is successful:

#' Information on Potential Pipeline
#'
#' This function figures out whether it is called from within a pipeline.
#' It does so by examining the parent evironment of the active system frames,
#' and whether any of these are the same as the enclosing environment of 
#' \code{\%>\%}.
#' 
#' @return A list with the values \code{is_piped} (logical) and \code{env}
#'   (an environment reference). The former is \code{TRUE} if a pipeline is
#'   identified as \code{FALSE} otherwise. The latter holds a reference to
#'   the \code{\%>\%} frame where the pipeline is created and evaluated.
#'
#' @noRd
pipeline_info <- function()
{
  parents <- lapply(sys.frames(), parent.env)

  is_magrittr_env <-
    vapply(parents, identical, logical(1), y = environment(`%>%`))

  is_piped <- any(is_magrittr_env)

  list(is_piped = is_piped, 
       env      = if (is_piped) sys.frames()[[min(which(is_magrittr_env))]])  
}


# This is just a jq placeholder.
# Here just squares the number
jq <- function(x) 
{
  print("jq Called!")

  x^2
}

#' Toggle Auto Execution On or Off for Pipelines
#'
#' A call to \code{pipe_autoexec} allows a function to toggle auto execution of
#' \code{jq} on or off at the end of a pipeline.
#' 
#' @param toggle logical: \code{TRUE} toggles auto execution on, \code{FALSE}
#'   toggles auto execution off.
#'
#' @details Once auto execution is turned on the \code{result} identifier inside
#' the pipeline is bound to an "Active Binding". This will not be changed on 
#' toggling auto execution off, but rather the function to be executed is 
#' changed to \code{identity}.
#'
#' @noRd  
pipe_autoexec <- function(toggle)
{
  if (!identical(toggle, TRUE) && !identical(toggle, FALSE)) {
    stop("Argument 'toggle' must be logical.")
  }

  info <- pipeline_info()

  if (isTRUE(info[["is_piped"]])) {
    pipeline_on_exit(info$env)
    info$env$.jq_exitfun <- if (toggle) jq else identity
  }

  invisible()
}

#' Setup On-Exit Action for a Pipeline
#'
#' A call to \code{pipeline_on_exit} will setup the pipeline for auto execution by 
#' making \code{result} inside \code{\%>\%} an active binding. The initial 
#' call will register the \code{identity} function as the exit action, 
#' but this can be changed to \code{jq} with a call to \code{pipe_autoexec}.
#' Subsequent calls to \code{pipeline_on_exit} has no effect.
#'  
#' @param env A reference to the \code{\%>\%} environment, in which 
#'   \code{result} is to be bound.
#'   
#' @noRd
pipeline_on_exit <- function(env)
{
  # Only activate the first time; after this the binding is already active.
  if (exists(".jq_exitfun", envir = env, inherits = FALSE, mode = "function")) {
    return(invisible())
  }
  env$.jq_exitfun <- identity

  res <- NULL

  jq_result <- function(v) 
  { 
    if (missing(v)) {
      res
    }
    else {
      res <<- `$<-`(v, value, env$.jq_exitfun(v$value))
    }
  }

  makeActiveBinding("result", jq_result, env)
}


# Example of function toggling auto execution on
f <- function(x)
{
  pipe_autoexec(toggle = TRUE)
  x
}

# Example of function toggling auto execution off
g <- function(x)
{
  pipe_autoexec(toggle = FALSE)
  x
}


10 %>% f %>% f %>% f
#> "jq Called"
#> 100

10 %>% f %>% f %>% g
#> 10

10 %>% f %>% g %>% f
#> "jq Called"
#> 100

f(f(f(10)))
#> 10

from jqr.

sckott avatar sckott commented on August 25, 2024

@smbache Awesome, thanks, will try this soon...

from jqr.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.