Skip to content

Commit

Permalink
fnshd intgrtng cstm errr fun fnctnlty (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonyfischetti committed Jun 4, 2015
1 parent 585ad8c commit 1fcc8b4
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 11 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: assertr
Type: Package
Title: Assertive Programming for R Analysis Pipelines
Version: 0.5
Version: 0.5.5
Authors@R: person("Tony", "Fischetti", email="tony.fischetti@gmail.com",
role = c("aut", "cre"))
Maintainer: Tony Fischetti <tony.fischetti@gmail.com>
Expand Down
4 changes: 4 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# assertr 0.5.5

* added support for parameterized error functions

# assertr 0.5

* improved performance by adding support for vectorized predicates
Expand Down
11 changes: 9 additions & 2 deletions R/assertions.R
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#' Uses dplyr's \code{select} to select
#' columns from data.
#' @param .dots Use assert_() to select columns using standard evaluation.
#' @param error_fun Function to call if assertion fails. Takes one error
#' string. Uses \code{stop} by default
#' @param .nameofpred Text representation of predicate for printing in case
#' of assertion violation. Will automatically be retrieved if left
#' blank (default)
Expand Down Expand Up @@ -125,6 +127,8 @@ assert_ <- function(data, predicate, ..., .dots, error_fun=assertr_stop,
#' Uses dplyr's \code{select} to select
#' columns from data.
#' @param .dots Use insist_() to select columns using standard evaluation.
#' @param error_fun Function to call if assertion fails. Takes one error
#' string. Uses \code{stop} by default
#' @param .nameofpred Text representation of predicate for printing in case
#' of assertion violation. Will automatically be retrieved if left
#' blank (default)
Expand Down Expand Up @@ -218,6 +222,8 @@ insist_ <- function(data, predicate_generator, ..., .dots,
#'
#' @param data A data frame, list, or environment
#' @param expr A logical expression
#' @param error_fun Function to call if assertion fails. Takes one error
#' string. Uses \code{stop} by default
#'
#' @return data if verification passes. error if not.
#' @note See \code{vignette("assertr")} for how to use this in context
Expand Down Expand Up @@ -252,7 +258,7 @@ insist_ <- function(data, predicate_generator, ..., .dots,
#'
#'
#' @export
verify <- function(data, expr){
verify <- function(data, expr, error_fun=stop){
expr <- substitute(expr)
# conform to terminology from subset
envir <- data
Expand All @@ -262,5 +268,6 @@ verify <- function(data, expr){
return(data)
num.violations <- sum(!logical.results)
error.message <- make.verify.error.message(num.violations)
stop(error.message)
error.message <- paste0(error.message, collapse = '')
error_fun(error.message)
}
49 changes: 49 additions & 0 deletions inst/doc/assertr.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,55 @@ Awesome! Now we can add an arbitrary number of assertions, as the need arises,
without touching the real logic.


### advanced: send email reports using custom error functions

One particularly cool application of `assertr` is to use it as a data integrity
checker for frequently updated data sources. A script can download new data as
it becomes available, and then run `assertr` checks on it. This makes assertr
into a sort of "continuous integration" tool (but for data,
not code.)

In an unsupervised "continuous integration" environment, you need a way to
discover that the assertions failed. In CI-as-a-service in the software world,
failed automated checks often send an email of reporting the maintainer of a
botched build; why not bring that functionality to `assertr`?!

All assertion verbs in `assertr` support a custom error function. By default,
the error function is R's built-in `stop` function, which displays a message
and halts execution. You can specify your own to hijack this behavior and
redirect flow-of-control wherever you want.

All custom error functions take one argument: the actual error message that
`assertr` assembles based on which assertions fail. In this case, we will
build a function that takes that message and emails it to someone before
halting execution. We will use the `mailR` package to send the mail.

```{r eval=FALSE, purl = FALSE}
library(mailR)
email_me <- function(err_str){
send.mail(from="assertr@gmail.com", to="YOU@gmail.com",
subject="error from assertr", body=err_str,
smtp = list(host.name="aspmx.l.google.com", port=25),
authenticate = FALSE, send=TRUE)
stop(err_str, call.=FALSE)
}
questionable_mtcars %>%
verify(nrow(.) > 10, error_fun=email_me) %>%
verify(mpg > 0, error_fun=email_me) %>%
insist(within_n_sds(4), mpg, error_fun=email_me) %>%
assert(in_set(0,1), am, vs, error_fun=email_me)
# ...
```

(this particular `send.mail` formulation will only for gmail receptients;
see the `mailR` documentation for more information)

Now you'll get notified of any failed assertions via email. Groovy!


### advanced: creating your own predicate generators for `insist`

`assertr` is build with robustness, correctness, and extensibility in mind.
Expand Down
29 changes: 27 additions & 2 deletions inst/doc/assertr.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<meta name="author" content="Tony Fischetti" />

<meta name="date" content="2015-05-26" />
<meta name="date" content="2015-06-03" />

<title>Assertive R Programming with assertr</title>

Expand Down Expand Up @@ -54,7 +54,7 @@
<div id="header">
<h1 class="title">Assertive R Programming with assertr</h1>
<h4 class="author"><em>Tony Fischetti</em></h4>
<h4 class="date"><em>2015-05-26</em></h4>
<h4 class="date"><em>2015-06-03</em></h4>
</div>


Expand Down Expand Up @@ -259,6 +259,31 @@ <h3>combining chains of assertions</h3>
## 3 8 15.10000</code></pre>
<p>Awesome! Now we can add an arbitrary number of assertions, as the need arises, without touching the real logic.</p>
</div>
<div id="advanced-send-email-reports-using-custom-error-functions" class="section level3">
<h3>advanced: send email reports using custom error functions</h3>
<p>One particularly cool application of <code>assertr</code> is to use it as a data integrity checker for frequently updated data sources. A script can download new data as it becomes available, and then run <code>assertr</code> checks on it. This makes assertr into a sort of “continuous integration” tool (but for data, not code.)</p>
<p>In an unsupervised “continuous integration” environment, you need a way to discover that the assertions failed. In CI-as-a-service in the software world, failed automated checks often send an email of reporting the maintainer of a botched build; why not bring that functionality to <code>assertr</code>?!</p>
<p>All assertion verbs in <code>assertr</code> support a custom error function. By default, the error function is R’s built-in <code>stop</code> function, which displays a message and halts execution. You can specify your own to hijack this behavior and redirect flow-of-control wherever you want.</p>
<p>All custom error functions take one argument: the actual error message that <code>assertr</code> assembles based on which assertions fail. In this case, we will build a function that takes that message and emails it to someone before halting execution. We will use the <code>mailR</code> package to send the mail.</p>
<pre class="sourceCode r"><code class="sourceCode r"><span class="kw">library</span>(mailR)

email_me &lt;-<span class="st"> </span>function(err_str){
<span class="kw">send.mail</span>(<span class="dt">from=</span><span class="st">&quot;assertr@gmail.com&quot;</span>, <span class="dt">to=</span><span class="st">&quot;YOU@gmail.com&quot;</span>,
<span class="dt">subject=</span><span class="st">&quot;error from assertr&quot;</span>, <span class="dt">body=</span>err_str,
<span class="dt">smtp =</span> <span class="kw">list</span>(<span class="dt">host.name=</span><span class="st">&quot;aspmx.l.google.com&quot;</span>, <span class="dt">port=</span><span class="dv">25</span>),
<span class="dt">authenticate =</span> <span class="ot">FALSE</span>, <span class="dt">send=</span><span class="ot">TRUE</span>)
<span class="kw">stop</span>(err_str, <span class="dt">call.=</span><span class="ot">FALSE</span>)
}

questionable_mtcars %&gt;%
<span class="st"> </span><span class="kw">verify</span>(<span class="kw">nrow</span>(.) &gt;<span class="st"> </span><span class="dv">10</span>, <span class="dt">error_fun=</span>email_me) %&gt;%
<span class="st"> </span><span class="kw">verify</span>(mpg &gt;<span class="st"> </span><span class="dv">0</span>, <span class="dt">error_fun=</span>email_me) %&gt;%
<span class="st"> </span><span class="kw">insist</span>(<span class="kw">within_n_sds</span>(<span class="dv">4</span>), mpg, <span class="dt">error_fun=</span>email_me) %&gt;%
<span class="st"> </span><span class="kw">assert</span>(<span class="kw">in_set</span>(<span class="dv">0</span>,<span class="dv">1</span>), am, vs, <span class="dt">error_fun=</span>email_me)
<span class="co"># ...</span></code></pre>
<p>(this particular <code>send.mail</code> formulation will only for gmail receptients; see the <code>mailR</code> documentation for more information)</p>
<p>Now you’ll get notified of any failed assertions via email. Groovy!</p>
</div>
<div id="advanced-creating-your-own-predicate-generators-for-insist" class="section level3">
<h3>advanced: creating your own predicate generators for <code>insist</code></h3>
<p><code>assertr</code> is build with robustness, correctness, and extensibility in mind. Just like <code>assertr</code> makes it easy to create your own custom predicates, so too does this package make it easy to create your own custom predicate generators.</p>
Expand Down
8 changes: 6 additions & 2 deletions man/assert.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
\alias{assert_}
\title{Raises error if predicate is FALSE in any columns selected}
\usage{
assert(data, predicate, ...)
assert(data, predicate, ..., error_fun = assertr_stop)

assert_(data, predicate, ..., .dots, .nameofpred = "")
assert_(data, predicate, ..., .dots, error_fun = assertr_stop,
.nameofpred = "")
}
\arguments{
\item{data}{A data frame}
Expand All @@ -18,6 +19,9 @@ assert_(data, predicate, ..., .dots, .nameofpred = "")
Uses dplyr's \code{select} to select
columns from data.}
\item{error_fun}{Function to call if assertion fails. Takes one error
string. Uses \code{stop} by default}
\item{.dots}{Use assert_() to select columns using standard evaluation.}
\item{.nameofpred}{Text representation of predicate for printing in case
Expand Down
8 changes: 6 additions & 2 deletions man/insist.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
\alias{insist_}
\title{Raises error if dynamically created predicate is FALSE in any columns selected}
\usage{
insist(data, predicate_generator, ...)
insist(data, predicate_generator, ..., error_fun = assertr_stop)

insist_(data, predicate_generator, ..., .dots, .nameofpred = "")
insist_(data, predicate_generator, ..., .dots, error_fun = assertr_stop,
.nameofpred = "")
}
\arguments{
\item{data}{A data frame}
Expand All @@ -21,6 +22,9 @@ every element in the column vectors selected}
Uses dplyr's \code{select} to select
columns from data.}
\item{error_fun}{Function to call if assertion fails. Takes one error
string. Uses \code{stop} by default}
\item{.dots}{Use insist_() to select columns using standard evaluation.}
\item{.nameofpred}{Text representation of predicate for printing in case
Expand Down
5 changes: 4 additions & 1 deletion man/verify.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
\alias{verify}
\title{Raises error if expression is FALSE anywhere}
\usage{
verify(data, expr)
verify(data, expr, error_fun = stop)
}
\arguments{
\item{data}{A data frame, list, or environment}

\item{expr}{A logical expression}

\item{error_fun}{Function to call if assertion fails. Takes one error
string. Uses \code{stop} by default}
}
\value{
data if verification passes. error if not.
Expand Down
16 changes: 15 additions & 1 deletion tests/testthat/test-assertions.R
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ test_that("verify breaks appropriately", {
"coercing argument of type 'double' to logical")
expect_error(suppressWarnings(verify(mtcars, "1")),
"missing value where TRUE/FALSE needed")
expect_error(verify(mtcars, 2 > 1, "tree"), "unused argument \\(\"tree\")")
expect_error(verify(mtcars, 0 > 1, "tree"), "could not find function \"error_fun\"")
expect_error(verify(mtcars, d > 1), "object 'd' not found")
})
######################################
Expand Down Expand Up @@ -146,13 +146,21 @@ test_that("assert raises *custom error* if verification fails (using se)", {
test_that("assert breaks appropriately", {
expect_error(assert(in_set(0,1), mtcars$vs),
"no applicable method for 'select.?' applied to an object of class \"function\"")
expect_error(assert(mtcars, in_set(0,1), vs, tree),
"object 'tree' not found")
expect_error(assert(mtcars, in_set(0,1), vs, "tree"),
"All select\\(\\) inputs must resolve to integer column positions")
expect_error(assert("tree"),
"no applicable method for 'select.?' applied to an object of class \"character\"")
})

test_that("assert breaks appropriately (using se)", {
expect_error(assert_(in_set(0,1), mtcars$vs),
"no applicable method for 'select.?' applied to an object of class \"function\"")
expect_error(assert_(mtcars, in_set(0,1), vs),
"object 'vs' not found")
expect_error(assert_(mtcars, in_set(0,1), "vs", "tree"),
"object 'tree' not found")
expect_error(assert_("tree"),
"no applicable method for 'select.?' applied to an object of class \"character\"")
})
Expand Down Expand Up @@ -232,6 +240,10 @@ test_that("insist raises *custom error* if verification fails (using se)", {
test_that("insist breaks appropriately", {
expect_error(insist(within_n_sds(5), mtcars$vs),
"no applicable method for 'select.?' applied to an object of class \"function\"")
expect_error(insist(mtcars, mtwithin_n_sds(5), "vs"),
"All select\\(\\) inputs must resolve to integer column positions")
expect_error(insist(mtcars, within_n_sds(5), tree),
"object 'tree' not found")
expect_error(insist("tree"),
"no applicable method for 'select.?' applied to an object of class \"character\"")
expect_error(insist(iris, within_n_sds(5), Petal.Width:Species),
Expand All @@ -241,6 +253,8 @@ test_that("insist breaks appropriately", {
test_that("insist breaks appropriately (using se)", {
expect_error(insist_(within_n_sds(5), "mtcars$vs"),
"no applicable method for 'select.?' applied to an object of class \"function\"")
expect_error(insist_(mtcars, within_n_sds(5), tree),
"object 'tree' not found")
expect_error(insist_("tree"),
"no applicable method for 'select.?' applied to an object of class \"character\"")
expect_error(insist_(iris, within_n_sds(5), "Petal.Width:Species"),
Expand Down
49 changes: 49 additions & 0 deletions vignettes/assertr.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,55 @@ Awesome! Now we can add an arbitrary number of assertions, as the need arises,
without touching the real logic.


### advanced: send email reports using custom error functions

One particularly cool application of `assertr` is to use it as a data integrity
checker for frequently updated data sources. A script can download new data as
it becomes available, and then run `assertr` checks on it. This makes assertr
into a sort of "continuous integration" tool (but for data,
not code.)

In an unsupervised "continuous integration" environment, you need a way to
discover that the assertions failed. In CI-as-a-service in the software world,
failed automated checks often send an email of reporting the maintainer of a
botched build; why not bring that functionality to `assertr`?!

All assertion verbs in `assertr` support a custom error function. By default,
the error function is R's built-in `stop` function, which displays a message
and halts execution. You can specify your own to hijack this behavior and
redirect flow-of-control wherever you want.

All custom error functions take one argument: the actual error message that
`assertr` assembles based on which assertions fail. In this case, we will
build a function that takes that message and emails it to someone before
halting execution. We will use the `mailR` package to send the mail.

```{r eval=FALSE, purl = FALSE}
library(mailR)
email_me <- function(err_str){
send.mail(from="assertr@gmail.com", to="YOU@gmail.com",
subject="error from assertr", body=err_str,
smtp = list(host.name="aspmx.l.google.com", port=25),
authenticate = FALSE, send=TRUE)
stop(err_str, call.=FALSE)
}
questionable_mtcars %>%
verify(nrow(.) > 10, error_fun=email_me) %>%
verify(mpg > 0, error_fun=email_me) %>%
insist(within_n_sds(4), mpg, error_fun=email_me) %>%
assert(in_set(0,1), am, vs, error_fun=email_me)
# ...
```

(this particular `send.mail` formulation will only for gmail receptients;
see the `mailR` documentation for more information)

Now you'll get notified of any failed assertions via email. Groovy!


### advanced: creating your own predicate generators for `insist`

`assertr` is build with robustness, correctness, and extensibility in mind.
Expand Down

0 comments on commit 1fcc8b4

Please sign in to comment.