{"action":"created","comment":{"url":"https://api.github.com/repos/rstudio/shiny/pulls/comments/202799570","pull_request_review_id":137566573,"id":202799570,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIwMjc5OTU3MA==","diff_hunk":"@@ -0,0 +1,344 @@\n+#' Create a memory cache object\n+#'\n+#' A memory cache object is a key-value store that saves the values in an\n+#' environment. Objects can be stored and retrieved using the \\code{get()} and\n+#' \\code{set()} methods. Objects are automatically pruned from the cache\n+#' according to the parameters \\code{max_size}, \\code{max_age}, \\code{max_n},\n+#' and \\code{evict}.\n+#'\n+#' In a \\code{MemoryCache}, R objects are stored directly in the cache; they are\n+#' not \\emph{not} serialized before being stored in the cache. This contrasts\n+#' with other cache types, like \\code{\\link{DiskCache}}, where objects are\n+#' serialized, and the serialized object is cached. This can result in some\n+#' differences of behavior. For example, as long as an object is stored in a\n+#' MemoryCache, it will not be garbage collected.\n+#'\n+#'\n+#' @section Missing keys:\n+#'   The \\code{missing} and \\code{exec_missing} parameters controls what happens\n+#'   when \\code{get()} is called with a key that is not in the cache (a cache\n+#'   miss). The default behavior is to return a \\code{\\link{key_missing}}\n+#'   object. This is a \\emph{sentinel value} that indicates that the key was not\n+#'   present in the cache. You can test if the returned value represents a\n+#'   missing key by using the \\code{\\link{is.key_missing}} function. You can\n+#'   also have \\code{get()} return a different sentinel value, like \\code{NULL}.\n+#'   If you want to throw an error on a cache miss, you can do so by providing a\n+#'   function for \\code{missing} that takes one argument, the key, and also use\n+#'   \\code{exec_missing=TRUE}.\n+#'\n+#'   When the cache is created, you can supply a value for \\code{missing}, which\n+#'   sets the default value to be returned for missing values. It can also be\n+#'   overridden when \\code{get()} is called, by supplying a \\code{missing}\n+#'   argument. For example, if you use \\code{cache$get(\"mykey\", missing =\n+#'   NULL)}, it will return \\code{NULL} if the key is not in the cache.\n+#'\n+#'   If your cache is configured so that \\code{get()} returns a sentinel value\n+#'   to represent a cache miss, then \\code{set} will also not allow you to store\n+#'   the sentinel value in the cache. It will throw an error if you attempt to\n+#'   do so.\n+#'\n+#'   Instead of returning the same sentinel value each time there is cache miss,\n+#'   the cache can execute a function each time \\code{get()} encounters missing\n+#'   key. If the function returns a value, then \\code{get()} will in turn return\n+#'   that value. However, a more common use is for the function to throw an\n+#'   error. If an error is thrown, then \\code{get()} will not return a value.\n+#'\n+#'   To do this, pass a one-argument function to \\code{missing}, and use\n+#'   \\code{exec_missing=TRUE}. For example, if you want to throw an error that\n+#'   prints the missing key, you could do this:\n+#'\n+#'   \\preformatted{\n+#'   diskCache(\n+#'     missing = function(key) {\n+#'       stop(\"Attempted to get missing key: \", key)\n+#'     },\n+#'     exec_missing = TRUE\n+#'   )\n+#'   }\n+#'\n+#'   If you use this, the code that calls \\code{get()} should be wrapped with\n+#'   \\code{\\link{tryCatch}()} to gracefully handle missing keys.\n+#'\n+#' @section Cache pruning:\n+#'\n+#'   Cache pruning occurs each time \\code{get()} and \\code{set()} are called, or\n+#'   it can be invoked manually by calling \\code{prune()}.\n+#'\n+#'   If there are any objects that are older than \\code{max_age}, they will be\n+#'   removed when a pruning occurs.\n+#'\n+#'   The \\code{max_size} and \\code{max_n} parameters are applied to the cache as\n+#'   a whole, in contrast to \\code{max_age}, which is applied to each object\n+#'   individually.\n+#'\n+#'   If the number of objects in the cache exceeds \\code{max_n}, then objects\n+#'   will be removed from the cache according to the eviction policy, which is\n+#'   set with the \\code{evict} parameter. Objects will be removed so that the\n+#'   number of items is \\code{max_n}.\n+#'\n+#'   If the size of the objects in the cache exceeds \\code{max_size}, then\n+#'   objects will be removed from the cache. Objects will be removed from the\n+#'   cache so that the total size remains under \\code{max_size}. The size is\n+#'   calculated by calling \\code{\\link{object.size}} on an object. Note that if\n+#'   two keys are associated with the same object, the size calculation will\n+#'   count the object's size twice, even though there is only one copy in\n+#'   memory.\n+#'\n+#' @section Eviction policies:\n+#'\n+#' If \\code{max_n} or \\code{max_size} are used, then objects will be removed\n+#' from the cache according to an eviction policy. The available eviction\n+#' policies are:\n+#'\n+#'   \\describe{\n+#'     \\item{\\code{\"lru\"}}{\n+#'       Least Recently Used. The least recently used objects will be removed.\n+#'       This uses the filesystem's atime property. Some filesystems do not\n+#'       support atime, or have a very low atime resolution. The DiskCache will\n+#'       check for atime support, and if the filesystem does not support atime,\n+#'       a warning will be issued and the \"fifo\" policy will be used instead.\n+#'     }\n+#'     \\item{\\code{\"fifo\"}}{\n+#'       First-in-first-out. The oldest objects will be removed.\n+#'     }\n+#'   }\n+#'\n+#' @section Methods:\n+#'\n+#'  A disk cache object has the following methods:\n+#'\n+#'   \\describe{\n+#'     \\item{\\code{get(key, missing, exec_missing)}}{\n+#'       Returns the value associated with \\code{key}. If the key is not in the\n+#'       cache, then it returns the value specified by \\code{missing} or,\n+#'       \\code{missing} is a function and \\code{exec_missing=TRUE}, then\n+#'       executes \\code{missing}. The function can throw an error or return the\n+#'       value. If either of these parameters are specified here, then they\n+#'       will override the defaults that were set when the DiskCache object was\n+#'       created. See section Missing Keys for more information.\n+#'     }\n+#'     \\item{\\code{set(key, value)}}{\n+#'       Stores the \\code{key}-\\code{value} pair in the cache.\n+#'     }\n+#'     \\item{\\code{exists(key)}}{\n+#'       Returns \\code{TRUE} if the cache contains the key, otherwise\n+#'       \\code{FALSE}.\n+#'     }\n+#'     \\item{\\code{size()}}{\n+#'       Returns the number of items currently in the cache.\n+#'     }\n+#'     \\item{\\code{keys()}}{\n+#'       Returns a character vector of all keys currently in the cache.\n+#'     }\n+#'     \\item{\\code{reset()}}{\n+#'       Clears all objects from the cache.\n+#'     }\n+#'     \\item{\\code{destroy()}}{\n+#'       Clears all objects in the cache, and removes the cache directory from\n+#'       disk.\n+#'     }\n+#'     \\item{\\code{prune()}}{\n+#'       Prunes the cache, using the parameters specified by \\code{max_size},\n+#'       \\code{max_age}, \\code{max_n}, and \\code{evict}.\n+#'     }\n+#'   }\n+#'\n+#' @inheritParams diskCache\n+#'\n+#' @export\n+memoryCache <- function(\n+  max_size = 10 * 1024 ^ 2,\n+  max_age = Inf,\n+  max_n = Inf,\n+  evict = c(\"lru\", \"fifo\"),\n+  missing = key_missing(),\n+  exec_missing = FALSE,\n+  logfile = NULL)\n+{\n+  MemoryCache$new(max_size, max_age, max_n, evict, missing, exec_missing, logfile)\n+}\n+\n+MemoryCache <- R6Class(\"MemoryCache\",\n+  public = list(\n+    initialize = function(\n+      max_size = 10 * 1024 ^ 2,\n+      max_age = Inf,\n+      max_n = Inf,\n+      evict = c(\"lru\", \"fifo\"),\n+      missing = key_missing(),\n+      exec_missing = FALSE,\n+      logfile = NULL)\n+    {\n+      if (exec_missing && (!is.function(missing) || length(formals(missing)) == 0)) {\n+        stop(\"When `exec_missing` is true, `missing` must be a function that takes one argument.\")\n+      }\n+      private$cache        <- new.env(parent = emptyenv())\n+      private$max_size     <- max_size\n+      private$max_age      <- max_age\n+      private$max_n        <- max_n\n+      private$evict        <- match.arg(evict)\n+      private$missing      <- missing\n+      private$exec_missing <- exec_missing\n+      private$logfile      <- logfile\n+    },\n+\n+    get = function(key, missing = private$missing, exec_missing = private$exec_missing) {\n+      private$log(paste0('get: key \"', key, '\"'))\n+      validate_key(key)\n+\n+      if (!self$exists(key)) {\n+        private$log(paste0('get: key \"', key, '\" is missing'))\n+        if (exec_missing) {\n+          if (!is.function(missing) || length(formals(missing)) == 0) {\n+            stop(\"When `exec_missing` is true, `missing` must be a function that takes one argument.\")\n+          }\n+          return(missing(key))\n+        } else {\n+          return(missing)\n+        }\n+      }\n+\n+      private$log(paste0('get: key \"', key, '\" found'))\n+      value <- private$cache[[key]]$value\n+      self$prune()\n+      value\n+    },\n+\n+    set = function(key, value) {\n+      private$log(paste0('set: key \"', key, '\"'))\n+      validate_key(key)\n+      if (!private$exec_missing && identical(value, private$missing)) {\n+        stop(\"Attempted to store sentinel value representing a missing key.\")\n+      }\n+\n+      time <- as.numeric(Sys.time())\n+      private$cache[[key]] <- list(\n+        key = key,\n+        value = value,\n+        size = as.numeric(object.size(value)),  # Reported size is rough! See ?object.size.\n+        mtime = time,\n+        atime = time\n+      )\n+      self$prune()\n+      invisible(self)\n+    },\n+\n+    exists = function(key) {\n+      validate_key(key)\n+      exists(key, envir = private$cache, inherits = FALSE)\n+    },\n+\n+    keys = function() {\n+      ls(private$cache, sorted = FALSE)  # Faster with sorted=FALSE\n+    },\n+\n+    remove = function(key) {\n+      private$log(paste0('remove: key \"', key, '\"'))\n+      validate_key(key)\n+      rm(list = key, envir = private$cache)\n+      invisible(self)\n+    },\n+\n+    reset = function() {\n+      private$log(paste0('reset'))\n+      rm(list = self$keys(), envir = private$cache)\n+      invisible(self)\n+    },\n+\n+    prune = function() {\n+      private$log(paste0('prune'))\n+      info <- private$object_info()\n+\n+      # 1. Remove any objects where the age exceeds max age.\n+      time <- as.numeric(Sys.time())\n+      timediff <- time - info$mtime\n+      rm_idx <- timediff > private$max_age\n+      if (any(rm_idx)) {\n+        private$log(paste0(\"prune max_age: Removing \", paste(info$key[rm_idx], collapse = \", \")))\n+      }\n+\n+      # Remove items from cache\n+      rm(list = info$key[rm_idx], envir = private$cache)\n+      info <- info[!rm_idx, ]","path":"R/cache-memory.R","position":262,"original_position":262,"commit_id":"6d37f6b4dd351326945e3da69d5af38c8eb8937e","original_commit_id":"6d37f6b4dd351326945e3da69d5af38c8eb8937e","user":{"login":"jcheng5","id":129551,"node_id":"MDQ6VXNlcjEyOTU1MQ==","avatar_url":"https://avatars0.githubusercontent.com/u/129551?v=4","gravatar_id":"","url":"https://api.github.com/users/jcheng5","html_url":"https://github.com/jcheng5","followers_url":"https://api.github.com/users/jcheng5/followers","following_url":"https://api.github.com/users/jcheng5/following{/other_user}","gists_url":"https://api.github.com/users/jcheng5/gists{/gist_id}","starred_url":"https://api.github.com/users/jcheng5/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jcheng5/subscriptions","organizations_url":"https://api.github.com/users/jcheng5/orgs","repos_url":"https://api.github.com/users/jcheng5/repos","events_url":"https://api.github.com/users/jcheng5/events{/privacy}","received_events_url":"https://api.github.com/users/jcheng5/received_events","type":"User","site_admin":false},"body":"Put these two lines in the previous if clause","created_at":"2018-07-16T19:38:54Z","updated_at":"2018-07-16T19:38:54Z","html_url":"https://github.com/rstudio/shiny/pull/1997#discussion_r202799570","pull_request_url":"https://api.github.com/repos/rstudio/shiny/pulls/1997","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/rstudio/shiny/pulls/comments/202799570"},"html":{"href":"https://github.com/rstudio/shiny/pull/1997#discussion_r202799570"},"pull_request":{"href":"https://api.github.com/repos/rstudio/shiny/pulls/1997"}}},"pull_request":{"url":"https://api.github.com/repos/rstudio/shiny/pulls/1997","id":179516551,"node_id":"MDExOlB1bGxSZXF1ZXN0MTc5NTE2NTUx","html_url":"https://github.com/rstudio/shiny/pull/1997","diff_url":"https://github.com/rstudio/shiny/pull/1997.diff","patch_url":"https://github.com/rstudio/shiny/pull/1997.patch","issue_url":"https://api.github.com/repos/rstudio/shiny/issues/1997","number":1997,"state":"open","locked":false,"title":"Add renderCachedPlot()","user":{"login":"wch","id":86978,"node_id":"MDQ6VXNlcjg2OTc4","avatar_url":"https://avatars3.githubusercontent.com/u/86978?v=4","gravatar_id":"","url":"https://api.github.com/users/wch","html_url":"https://github.com/wch","followers_url":"https://api.github.com/users/wch/followers","following_url":"https://api.github.com/users/wch/following{/other_user}","gists_url":"https://api.github.com/users/wch/gists{/gist_id}","starred_url":"https://api.github.com/users/wch/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wch/subscriptions","organizations_url":"https://api.github.com/users/wch/orgs","repos_url":"https://api.github.com/users/wch/repos","events_url":"https://api.github.com/users/wch/events{/privacy}","received_events_url":"https://api.github.com/users/wch/received_events","type":"User","site_admin":false},"body":"This adds `renderCachedPlot()`, which is like `renderPlot()`, but caches the plots to disk. The cache can scoped to a session, to one run of an app (essentially, one call of `runApp`, which can service multiple user sessions), or the app author can specify a cache directory and control the lifetime of the cache.\n\nThe base size for cached plot is 400x400 pixels. It will allow other aspect ratios, in multiples of 1.2 by default, although the multiplier can be changed. This means that it will create and cache plots at aspect ratio of 1:1, 1:1.2, 1:1.44, and so on, as well as 1.2:1, 1.44:1, and so on.\n\nIt will also cache different sizes, in multiples of 1.2 by default. So for the 1:1 ratio it will cache images at 278x278, 333x333, 400x400, 480x480, 576x576, and so on.\n\nMinimal usage example:\n\n```R\ndata <- reactiveVal(data.frame(x = rnorm(400), y = rnorm(400)))\n\nui <- fluidPage(\n  sidebarLayout(\n    sidebarPanel(\n      sliderInput(\"n\", \"Number of points\", 50, 400, 100, step = 50)\n    ),\n    mainPanel(\n      plotOutput(\"plot\")\n    )\n  )\n)\n\nserver <- function(input, output, session) {\n  output$plot <- renderCachedPlot(\n    {\n      cat(\"User code\\n\")\n      Sys.sleep(1.5)\n      d <- data()\n      seqn <- seq_len(input$n)\n      plot(d$x[seqn], d$y[seqn], xlim = range(d$x), ylim = range(d$y))\n    },\n    cacheKeyExpr = { list(input$n) },\n    cacheResetEventExpr = { data() }\n  )\n}\n\nshinyApp(ui, server)\n```\n\n\nA slightly more complex example, which also adds the ability to get a new data set and invalidate the cache.\n\n```R\ndata <- reactiveVal(data.frame(x = rnorm(400), y = rnorm(400)))\n\nui <- fluidPage(\n  sidebarLayout(\n    sidebarPanel(\n      sliderInput(\"n\", \"Number of points\", 50, 400, 100, step = 50),\n      actionButton(\"newdata\", \"New data\")\n    ),\n    mainPanel(\n      plotOutput(\"plot\")\n    )\n  )\n)\n\nserver <- function(input, output, session) {\n  observeEvent(input$newdata, {\n    cat(\"Newdata\\n\")\n    data(data.frame(x = rnorm(400), y = rnorm(400)))\n  })\n\n  output$plot <- renderCachedPlot(\n    {\n      cat(\"User code\\n\")\n      Sys.sleep(1.5)\n      d <- data()\n      seqn <- seq_len(input$n)\n      plot(d$x[seqn], d$y[seqn], xlim = range(d$x), ylim = range(d$y))\n    },\n    cacheKeyExpr = {\n      list(input$n)\n    },\n    cacheResetEventExpr = {\n      cat(\"invalidation\\n\")\n      data()\n    },\n    scope = \"app\"\n  )\n}\n\nshinyApp(ui, server)\n```","created_at":"2018-04-04T21:23:54Z","updated_at":"2018-07-16T19:38:54Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5fddfeee92284aca160eb8d01e9170a5839e0371","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/rstudio/shiny/pulls/1997/commits","review_comments_url":"https://api.github.com/repos/rstudio/shiny/pulls/1997/comments","review_comment_url":"https://api.github.com/repos/rstudio/shiny/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rstudio/shiny/issues/1997/comments","statuses_url":"https://api.github.com/repos/rstudio/shiny/statuses/6d37f6b4dd351326945e3da69d5af38c8eb8937e","head":{"label":"rstudio:plot-cache","ref":"plot-cache","sha":"6d37f6b4dd351326945e3da69d5af38c8eb8937e","user":{"login":"rstudio","id":513560,"node_id":"MDEyOk9yZ2FuaXphdGlvbjUxMzU2MA==","avatar_url":"https://avatars1.githubusercontent.com/u/513560?v=4","gravatar_id":"","url":"https://api.github.com/users/rstudio","html_url":"https://github.com/rstudio","followers_url":"https://api.github.com/users/rstudio/followers","following_url":"https://api.github.com/users/rstudio/following{/other_user}","gists_url":"https://api.github.com/users/rstudio/gists{/gist_id}","starred_url":"https://api.github.com/users/rstudio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rstudio/subscriptions","organizations_url":"https://api.github.com/users/rstudio/orgs","repos_url":"https://api.github.com/users/rstudio/repos","events_url":"https://api.github.com/users/rstudio/events{/privacy}","received_events_url":"https://api.github.com/users/rstudio/received_events","type":"Organization","site_admin":false},"repo":{"id":4729944,"node_id":"MDEwOlJlcG9zaXRvcnk0NzI5OTQ0","name":"shiny","full_name":"rstudio/shiny","owner":{"login":"rstudio","id":513560,"node_id":"MDEyOk9yZ2FuaXphdGlvbjUxMzU2MA==","avatar_url":"https://avatars1.githubusercontent.com/u/513560?v=4","gravatar_id":"","url":"https://api.github.com/users/rstudio","html_url":"https://github.com/rstudio","followers_url":"https://api.github.com/users/rstudio/followers","following_url":"https://api.github.com/users/rstudio/following{/other_user}","gists_url":"https://api.github.com/users/rstudio/gists{/gist_id}","starred_url":"https://api.github.com/users/rstudio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rstudio/subscriptions","organizations_url":"https://api.github.com/users/rstudio/orgs","repos_url":"https://api.github.com/users/rstudio/repos","events_url":"https://api.github.com/users/rstudio/events{/privacy}","received_events_url":"https://api.github.com/users/rstudio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rstudio/shiny","description":"Easy interactive web applications with R","fork":false,"url":"https://api.github.com/repos/rstudio/shiny","forks_url":"https://api.github.com/repos/rstudio/shiny/forks","keys_url":"https://api.github.com/repos/rstudio/shiny/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rstudio/shiny/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rstudio/shiny/teams","hooks_url":"https://api.github.com/repos/rstudio/shiny/hooks","issue_events_url":"https://api.github.com/repos/rstudio/shiny/issues/events{/number}","events_url":"https://api.github.com/repos/rstudio/shiny/events","assignees_url":"https://api.github.com/repos/rstudio/shiny/assignees{/user}","branches_url":"https://api.github.com/repos/rstudio/shiny/branches{/branch}","tags_url":"https://api.github.com/repos/rstudio/shiny/tags","blobs_url":"https://api.github.com/repos/rstudio/shiny/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rstudio/shiny/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rstudio/shiny/git/refs{/sha}","trees_url":"https://api.github.com/repos/rstudio/shiny/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rstudio/shiny/statuses/{sha}","languages_url":"https://api.github.com/repos/rstudio/shiny/languages","stargazers_url":"https://api.github.com/repos/rstudio/shiny/stargazers","contributors_url":"https://api.github.com/repos/rstudio/shiny/contributors","subscribers_url":"https://api.github.com/repos/rstudio/shiny/subscribers","subscription_url":"https://api.github.com/repos/rstudio/shiny/subscription","commits_url":"https://api.github.com/repos/rstudio/shiny/commits{/sha}","git_commits_url":"https://api.github.com/repos/rstudio/shiny/git/commits{/sha}","comments_url":"https://api.github.com/repos/rstudio/shiny/comments{/number}","issue_comment_url":"https://api.github.com/repos/rstudio/shiny/issues/comments{/number}","contents_url":"https://api.github.com/repos/rstudio/shiny/contents/{+path}","compare_url":"https://api.github.com/repos/rstudio/shiny/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rstudio/shiny/merges","archive_url":"https://api.github.com/repos/rstudio/shiny/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rstudio/shiny/downloads","issues_url":"https://api.github.com/repos/rstudio/shiny/issues{/number}","pulls_url":"https://api.github.com/repos/rstudio/shiny/pulls{/number}","milestones_url":"https://api.github.com/repos/rstudio/shiny/milestones{/number}","notifications_url":"https://api.github.com/repos/rstudio/shiny/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rstudio/shiny/labels{/name}","releases_url":"https://api.github.com/repos/rstudio/shiny/releases{/id}","deployments_url":"https://api.github.com/repos/rstudio/shiny/deployments","created_at":"2012-06-20T18:45:11Z","updated_at":"2018-07-16T14:01:28Z","pushed_at":"2018-07-12T20:20:38Z","git_url":"git://github.com/rstudio/shiny.git","ssh_url":"git@github.com:rstudio/shiny.git","clone_url":"https://github.com/rstudio/shiny.git","svn_url":"https://github.com/rstudio/shiny","homepage":"http://shiny.rstudio.com","size":22921,"stargazers_count":2942,"watchers_count":2942,"language":"R","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1396,"mirror_url":null,"archived":false,"open_issues_count":387,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":1396,"open_issues":387,"watchers":2942,"default_branch":"master"}},"base":{"label":"rstudio:master","ref":"master","sha":"81cc7c591e263c6539b534d43a9cc8691d7160ee","user":{"login":"rstudio","id":513560,"node_id":"MDEyOk9yZ2FuaXphdGlvbjUxMzU2MA==","avatar_url":"https://avatars1.githubusercontent.com/u/513560?v=4","gravatar_id":"","url":"https://api.github.com/users/rstudio","html_url":"https://github.com/rstudio","followers_url":"https://api.github.com/users/rstudio/followers","following_url":"https://api.github.com/users/rstudio/following{/other_user}","gists_url":"https://api.github.com/users/rstudio/gists{/gist_id}","starred_url":"https://api.github.com/users/rstudio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rstudio/subscriptions","organizations_url":"https://api.github.com/users/rstudio/orgs","repos_url":"https://api.github.com/users/rstudio/repos","events_url":"https://api.github.com/users/rstudio/events{/privacy}","received_events_url":"https://api.github.com/users/rstudio/received_events","type":"Organization","site_admin":false},"repo":{"id":4729944,"node_id":"MDEwOlJlcG9zaXRvcnk0NzI5OTQ0","name":"shiny","full_name":"rstudio/shiny","owner":{"login":"rstudio","id":513560,"node_id":"MDEyOk9yZ2FuaXphdGlvbjUxMzU2MA==","avatar_url":"https://avatars1.githubusercontent.com/u/513560?v=4","gravatar_id":"","url":"https://api.github.com/users/rstudio","html_url":"https://github.com/rstudio","followers_url":"https://api.github.com/users/rstudio/followers","following_url":"https://api.github.com/users/rstudio/following{/other_user}","gists_url":"https://api.github.com/users/rstudio/gists{/gist_id}","starred_url":"https://api.github.com/users/rstudio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rstudio/subscriptions","organizations_url":"https://api.github.com/users/rstudio/orgs","repos_url":"https://api.github.com/users/rstudio/repos","events_url":"https://api.github.com/users/rstudio/events{/privacy}","received_events_url":"https://api.github.com/users/rstudio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rstudio/shiny","description":"Easy interactive web applications with R","fork":false,"url":"https://api.github.com/repos/rstudio/shiny","forks_url":"https://api.github.com/repos/rstudio/shiny/forks","keys_url":"https://api.github.com/repos/rstudio/shiny/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rstudio/shiny/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rstudio/shiny/teams","hooks_url":"https://api.github.com/repos/rstudio/shiny/hooks","issue_events_url":"https://api.github.com/repos/rstudio/shiny/issues/events{/number}","events_url":"https://api.github.com/repos/rstudio/shiny/events","assignees_url":"https://api.github.com/repos/rstudio/shiny/assignees{/user}","branches_url":"https://api.github.com/repos/rstudio/shiny/branches{/branch}","tags_url":"https://api.github.com/repos/rstudio/shiny/tags","blobs_url":"https://api.github.com/repos/rstudio/shiny/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rstudio/shiny/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rstudio/shiny/git/refs{/sha}","trees_url":"https://api.github.com/repos/rstudio/shiny/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rstudio/shiny/statuses/{sha}","languages_url":"https://api.github.com/repos/rstudio/shiny/languages","stargazers_url":"https://api.github.com/repos/rstudio/shiny/stargazers","contributors_url":"https://api.github.com/repos/rstudio/shiny/contributors","subscribers_url":"https://api.github.com/repos/rstudio/shiny/subscribers","subscription_url":"https://api.github.com/repos/rstudio/shiny/subscription","commits_url":"https://api.github.com/repos/rstudio/shiny/commits{/sha}","git_commits_url":"https://api.github.com/repos/rstudio/shiny/git/commits{/sha}","comments_url":"https://api.github.com/repos/rstudio/shiny/comments{/number}","issue_comment_url":"https://api.github.com/repos/rstudio/shiny/issues/comments{/number}","contents_url":"https://api.github.com/repos/rstudio/shiny/contents/{+path}","compare_url":"https://api.github.com/repos/rstudio/shiny/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rstudio/shiny/merges","archive_url":"https://api.github.com/repos/rstudio/shiny/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rstudio/shiny/downloads","issues_url":"https://api.github.com/repos/rstudio/shiny/issues{/number}","pulls_url":"https://api.github.com/repos/rstudio/shiny/pulls{/number}","milestones_url":"https://api.github.com/repos/rstudio/shiny/milestones{/number}","notifications_url":"https://api.github.com/repos/rstudio/shiny/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rstudio/shiny/labels{/name}","releases_url":"https://api.github.com/repos/rstudio/shiny/releases{/id}","deployments_url":"https://api.github.com/repos/rstudio/shiny/deployments","created_at":"2012-06-20T18:45:11Z","updated_at":"2018-07-16T14:01:28Z","pushed_at":"2018-07-12T20:20:38Z","git_url":"git://github.com/rstudio/shiny.git","ssh_url":"git@github.com:rstudio/shiny.git","clone_url":"https://github.com/rstudio/shiny.git","svn_url":"https://github.com/rstudio/shiny","homepage":"http://shiny.rstudio.com","size":22921,"stargazers_count":2942,"watchers_count":2942,"language":"R","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":1396,"mirror_url":null,"archived":false,"open_issues_count":387,"license":{"key":"other","name":"Other","spdx_id":null,"url":null,"node_id":"MDc6TGljZW5zZTA="},"forks":1396,"open_issues":387,"watchers":2942,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rstudio/shiny/pulls/1997"},"html":{"href":"https://github.com/rstudio/shiny/pull/1997"},"issue":{"href":"https://api.github.com/repos/rstudio/shiny/issues/1997"},"comments":{"href":"https://api.github.com/repos/rstudio/shiny/issues/1997/comments"},"review_comments":{"href":"https://api.github.com/repos/rstudio/shiny/pulls/1997/comments"},"review_comment":{"href":"https://api.github.com/repos/rstudio/shiny/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rstudio/shiny/pulls/1997/commits"},"statuses":{"href":"https://api.github.com/repos/rstudio/shiny/statuses/6d37f6b4dd351326945e3da69d5af38c8eb8937e"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"jcheng5"}}