

{"id":4490,"date":"2016-07-05T08:15:06","date_gmt":"2016-07-05T13:15:06","guid":{"rendered":"http:\/\/rud.is\/b\/?p=4490"},"modified":"2018-03-07T16:42:22","modified_gmt":"2018-03-07T21:42:22","slug":"a-simple-prediction-web-service-using-the-new-firery-package","status":"publish","type":"post","link":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/","title":{"rendered":"A Simple Prediction Web Service Using the New fiery Package"},"content":{"rendered":"<p>[`fiery`](https:\/\/github.com\/thomasp85\/fiery) is a new `Rook`\/`httuv`-based R web server in town created by @thomasp85 that aims to fill the gap between raw http &#038; websockets and Shiny with a flexible framework for handling requests and serving up responses. <\/p>\n<p>The intent of this post is to provide a quick-start to using it setup a prediction API service.<\/p>\n<p>We&#8217;ll be using the _super complex model_ described in the first example of the `predict.lm` manual page and save the fitted model out so we can load it up in the web server and use it for predicting values from inputs.<\/p>\n<pre id=\"ex1\"><code class=\"language-r\">set.seed(1492)\r\nx &lt;- rnorm(15)\r\ny &lt;- x + rnorm(15)\r\nfit &lt;- lm(y ~ x)\r\nsaveRDS(fit, &quot;model.rds&quot;)<\/code><\/pre>\n<p>The code is annotated, but the gist is to:<\/p>\n<p>&#8211; Fire up the server (NOTE: it puts itself on `0.0.0.0` _by default_ so *CHANGE THIS* until you&#8217;re ready for production)<br \/>\n&#8211; Load the saved model<br \/>\n&#8211; Setup the routing for the requests<br \/>\n&#8211; Send back the model as JSON (since it&#8217;s an API vs something meant for humans)<\/p>\n<p>Here&#8217;s the code (jump past it for more info):<\/p>\n<pre id=\"prism-r-code\"><code class=\"language-r\">suppressPackageStartupMessages(library(fiery))\r\nsuppressPackageStartupMessages(library(utils))\r\nsuppressPackageStartupMessages(library(jsonlite))\r\nsuppressPackageStartupMessages(library(shiny))\r\n\r\napp &lt;- Fire$new()\r\n\r\n# This is absolutely necessary unless you&#039;re deliberately trying\r\n# to expose the service to the entire network you are on which\r\n# you probably don&#039;t want to do until in test \/ stage \/ prod\r\n\r\napp$host &lt;- &quot;127.0.0.1&quot;\r\napp$port &lt;- 9123 # completely arbitrary selection, make it whatevs\r\n\r\nmodel &lt;- NULL\r\n\r\n# When the app starts, we&#039;ll load the model we saved. This\r\n# particular one is just the first example on ?predict.lm.\r\n# This doesn&#039;t have to be global, per se, but this makes\r\n# for a quick example of how to setup an model API server\r\n\r\napp$on(&quot;start&quot;, function(server, ...) {\r\n  message(sprintf(&quot;Running on %s:%s&quot;, app$host, app$port))\r\n  model &lt;&lt;- readRDS(&quot;model.rds&quot;)\r\n  message(&quot;Model loaded&quot;)\r\n})\r\n\r\n# when the request comes in, route it properly. this will\r\n# be *much* nicer with Thomas&#039; `routr` plugin, but you can\r\n# get up and running now this way until it&#039;s fully documented\r\n# and on CRAN.\r\n#\r\n# 3 routes:\r\n#\r\n# if &quot;\/&quot; then return an empty HTML page\r\n# if &quot;\/info&quot; give some data about the server (just for example purposes)\r\n# if &quot;\/predict?val=##&quot; run the value through the model\r\n#\r\n# No error checking or anything as this is (again) a simple\r\n# example\r\n\r\napp$on(&#039;request&#039;, function(server, id, request, ...) {\r\n\r\n  response &lt;- list(\r\n    status = 200L,\r\n    headers = list(&#039;Content-Type&#039;=&#039;text\/html&#039;),\r\n    body = &quot;&quot;\r\n  )\r\n\r\n  # this helps us see what the path is\r\n  path &lt;- get(&quot;PATH_INFO&quot;, envir=request)\r\n  if (path == &quot;\/info&quot;) {\r\n\r\n    # Build a list of all the request headers so we can \r\n    # regurgitate them\r\n\r\n    out &lt;- sapply(grep(&quot;^[A-Z_0-9]+&quot;, names(request), value=TRUE), function(x) {\r\n      sprintf(&quot;%s: %s&quot;, x, get(x, envir=request))\r\n    })\r\n    out &lt;- paste0(out, collapse=&quot;\\n&quot;)\r\n\r\n    response$body &lt;- sprintf(&quot;&lt;pre&gt;Connection Id: %s\\n\\n%s&lt;\/pre&gt;&quot;, id, out)\r\n\r\n  } else if (grepl(&quot;^\/predict&quot;, path)) {\r\n\r\n    # this gets the query string; we&#039;re expecting val=##\r\n    # but aren&#039;t going to do any error checking here.\r\n    # You also would want to ensure there is nothing \r\n    # malicious in the query string.\r\n    query  &lt;- get(&quot;QUERY_STRING&quot;, envir=request)\r\n\r\n    # handy helper function from the Shiny folks\r\n    input &lt;- shiny::parseQueryString(query)\r\n\r\n    message(sprintf(&quot;Input: %s&quot;, input$val))\r\n\r\n    # run the prediction and add the input var value to the list\r\n    res &lt;- predict(model, data.frame(x=as.numeric(input$val)), se.fit = TRUE)\r\n    res$INPUT &lt;- input$val\r\n\r\n    # we want to return JSON\r\n    response$headers &lt;- list(&quot;Content-Type&quot;=&quot;application\/json&quot;)\r\n    response$body &lt;- jsonlite::toJSON(res, auto_unbox=TRUE, pretty=TRUE)\r\n\r\n  }\r\n\r\n  response\r\n\r\n})\r\n\r\n# don&#039;t fire off a browser call\r\napp$ignite(showcase=FALSE)\r\n<\/code><\/pre>\n<p>Assuming you&#8217;ve saved that as `modelserver.r`, you can fire that up in R\/RStudio-proper or on the command-line with `Rscript modelserver.r` (also assuming the fitted model RDS file is in the same directory which is prbly not a good idea for production as well). <\/p>\n<p>You can either enter something like `http:\/\/127.0.0.1:9123\/predict?val=-1.5` into your browser to see the JSON result there ore use `cURL`:<\/p>\n<pre id=\"curlex\"><code class=\"language-bash\">$ curl http:\/\/127.0.0.1:9123\/predict?val=-1.5\r\n{\r\n  &quot;fit&quot;: -0.8545,\r\n  &quot;se.fit&quot;: 0.5116,\r\n  &quot;df&quot;: 13,\r\n  &quot;residual.scale&quot;: 1.1088,\r\n  &quot;INPUT&quot;: &quot;-1.5&quot;\r\n}<\/code><\/pre>\n<p>or even `httr`:<\/p>\n<pre id=\"httrex\"><code class=\"language-r\">httr::content(httr::GET(&quot;http:\/\/127.0.0.1:9123\/predict?val=-1.5&quot;))\r\n$fit\r\n[1] -0.8545\r\n\r\n$se.fit\r\n[1] 0.5116\r\n\r\n$df\r\n[1] 13\r\n\r\n$residual.scale\r\n[1] 1.1088\r\n\r\n$INPUT\r\n[1] &quot;-1.5&quot;<\/code><\/pre>\n<p>Try hitting `http:\/\/127.0.0.1:9123\/` and `http:\/\/127.0.0.1:9123\/info` in similar ways to see what you get.<\/p>\n<p>Keep a watchful eye on [`routr`](https:\/\/github.com\/thomasp85\/routr) as it will make setting up API servers in R much easier than this. So far I&#8217;m finding `fiery` a nice middle-ground between writing raw `httuv` servers, abusing Shiny (since it&#8217;s really meant for UX work) or dealing with the slightly more complex `opencpu` package for turning R into a web request handling engine.<\/p>\n<p>Ideally, one would put this behind a security-aware reverse proxy for both safety (you can add some web application firewall-ish rules) and load balancing, but for in-house\/local testing, this is a super quick way to publish your models for wider use. Depending on the adoption rate of `fiery`, I&#8217;ll create some future posts that deal with the complexities of security and performance, along with how to put this all into something like Docker for rapid, controlled deployments.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>[`fiery`](https:\/\/github.com\/thomasp85\/fiery) is a new `Rook`\/`httuv`-based R web server in town created by @thomasp85 that aims to fill the gap between raw http &#038; websockets and Shiny with a flexible framework for handling requests and serving up responses. The intent of this post is to provide a quick-start to using it setup a prediction API service. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"activitypub_content_warning":"","activitypub_content_visibility":"","activitypub_max_image_attachments":3,"activitypub_interaction_policy_quote":"anyone","activitypub_status":"","footnotes":""},"categories":[91],"tags":[810],"class_list":["post-4490","post","type-post","status-publish","format-standard","hentry","category-r","tag-post"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>A Simple Prediction Web Service Using the New fiery Package - rud.is<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"A Simple Prediction Web Service Using the New fiery Package - rud.is\" \/>\n<meta property=\"og:description\" content=\"[`fiery`](https:\/\/github.com\/thomasp85\/fiery) is a new `Rook`\/`httuv`-based R web server in town created by @thomasp85 that aims to fill the gap between raw http &#038; websockets and Shiny with a flexible framework for handling requests and serving up responses. The intent of this post is to provide a quick-start to using it setup a prediction API service. [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/\" \/>\n<meta property=\"og:site_name\" content=\"rud.is\" \/>\n<meta property=\"article:published_time\" content=\"2016-07-05T13:15:06+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-03-07T21:42:22+00:00\" \/>\n<meta name=\"author\" content=\"hrbrmstr\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"hrbrmstr\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"2 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2016\\\/07\\\/05\\\/a-simple-prediction-web-service-using-the-new-firery-package\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2016\\\/07\\\/05\\\/a-simple-prediction-web-service-using-the-new-firery-package\\\/\"},\"author\":{\"name\":\"hrbrmstr\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\"},\"headline\":\"A Simple Prediction Web Service Using the New fiery Package\",\"datePublished\":\"2016-07-05T13:15:06+00:00\",\"dateModified\":\"2018-03-07T21:42:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2016\\\/07\\\/05\\\/a-simple-prediction-web-service-using-the-new-firery-package\\\/\"},\"wordCount\":435,\"commentCount\":14,\"publisher\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\"},\"keywords\":[\"post\"],\"articleSection\":[\"R\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/rud.is\\\/b\\\/2016\\\/07\\\/05\\\/a-simple-prediction-web-service-using-the-new-firery-package\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2016\\\/07\\\/05\\\/a-simple-prediction-web-service-using-the-new-firery-package\\\/\",\"url\":\"https:\\\/\\\/rud.is\\\/b\\\/2016\\\/07\\\/05\\\/a-simple-prediction-web-service-using-the-new-firery-package\\\/\",\"name\":\"A Simple Prediction Web Service Using the New fiery Package - rud.is\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#website\"},\"datePublished\":\"2016-07-05T13:15:06+00:00\",\"dateModified\":\"2018-03-07T21:42:22+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2016\\\/07\\\/05\\\/a-simple-prediction-web-service-using-the-new-firery-package\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/rud.is\\\/b\\\/2016\\\/07\\\/05\\\/a-simple-prediction-web-service-using-the-new-firery-package\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/2016\\\/07\\\/05\\\/a-simple-prediction-web-service-using-the-new-firery-package\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/rud.is\\\/b\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"A Simple Prediction Web Service Using the New fiery Package\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#website\",\"url\":\"https:\\\/\\\/rud.is\\\/b\\\/\",\"name\":\"rud.is\",\"description\":\"&quot;In God we trust. All others must bring data&quot;\",\"publisher\":{\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/rud.is\\\/b\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/rud.is\\\/b\\\/#\\\/schema\\\/person\\\/d7cb7487ab0527447f7fda5c423ff886\",\"name\":\"hrbrmstr\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\",\"contentUrl\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\",\"width\":460,\"height\":460,\"caption\":\"hrbrmstr\"},\"logo\":{\"@id\":\"https:\\\/\\\/i0.wp.com\\\/rud.is\\\/b\\\/wp-content\\\/uploads\\\/2023\\\/10\\\/ukr-shield.png?fit=460%2C460&ssl=1\"},\"description\":\"Don't look at me\u2026I do what he does \u2014 just slower. #rstats avuncular \u2022 ?Resistance Fighter \u2022 Cook \u2022 Christian \u2022 [Master] Chef des Donn\u00e9es de S\u00e9curit\u00e9 @ @rapid7\",\"sameAs\":[\"http:\\\/\\\/rud.is\"],\"url\":\"https:\\\/\\\/rud.is\\\/b\\\/author\\\/hrbrmstr\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"A Simple Prediction Web Service Using the New fiery Package - rud.is","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/","og_locale":"en_US","og_type":"article","og_title":"A Simple Prediction Web Service Using the New fiery Package - rud.is","og_description":"[`fiery`](https:\/\/github.com\/thomasp85\/fiery) is a new `Rook`\/`httuv`-based R web server in town created by @thomasp85 that aims to fill the gap between raw http &#038; websockets and Shiny with a flexible framework for handling requests and serving up responses. The intent of this post is to provide a quick-start to using it setup a prediction API service. [&hellip;]","og_url":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/","og_site_name":"rud.is","article_published_time":"2016-07-05T13:15:06+00:00","article_modified_time":"2018-03-07T21:42:22+00:00","author":"hrbrmstr","twitter_card":"summary_large_image","twitter_misc":{"Written by":"hrbrmstr","Est. reading time":"2 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/#article","isPartOf":{"@id":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/"},"author":{"name":"hrbrmstr","@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"headline":"A Simple Prediction Web Service Using the New fiery Package","datePublished":"2016-07-05T13:15:06+00:00","dateModified":"2018-03-07T21:42:22+00:00","mainEntityOfPage":{"@id":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/"},"wordCount":435,"commentCount":14,"publisher":{"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"keywords":["post"],"articleSection":["R"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/","url":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/","name":"A Simple Prediction Web Service Using the New fiery Package - rud.is","isPartOf":{"@id":"https:\/\/rud.is\/b\/#website"},"datePublished":"2016-07-05T13:15:06+00:00","dateModified":"2018-03-07T21:42:22+00:00","breadcrumb":{"@id":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/rud.is\/b\/2016\/07\/05\/a-simple-prediction-web-service-using-the-new-firery-package\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/rud.is\/b\/"},{"@type":"ListItem","position":2,"name":"A Simple Prediction Web Service Using the New fiery Package"}]},{"@type":"WebSite","@id":"https:\/\/rud.is\/b\/#website","url":"https:\/\/rud.is\/b\/","name":"rud.is","description":"&quot;In God we trust. All others must bring data&quot;","publisher":{"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/rud.is\/b\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886","name":"hrbrmstr","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","url":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","contentUrl":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1","width":460,"height":460,"caption":"hrbrmstr"},"logo":{"@id":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2023\/10\/ukr-shield.png?fit=460%2C460&ssl=1"},"description":"Don't look at me\u2026I do what he does \u2014 just slower. #rstats avuncular \u2022 ?Resistance Fighter \u2022 Cook \u2022 Christian \u2022 [Master] Chef des Donn\u00e9es de S\u00e9curit\u00e9 @ @rapid7","sameAs":["http:\/\/rud.is"],"url":"https:\/\/rud.is\/b\/author\/hrbrmstr\/"}]}},"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p23idr-1aq","jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":12497,"url":"https:\/\/rud.is\/b\/2019\/09\/14\/rswitch-1-5-0-release-now-also-corrals-rstudio-server-connections\/","url_meta":{"origin":4490,"position":0},"title":"RSwitch 1.5.0 Release Now Also Corrals RStudio Server Connections","author":"hrbrmstr","date":"2019-09-14","format":false,"excerpt":"RSwitch is a macOS menubar application that works on macOS 10.14+ and provides handy shortcuts for developing with R on macOS. Version 1.5.0 brings a reorganized menu system and the ability to manage and make connections to RStudio Server instances. Here's a quick peek at the new setup: All books,\u2026","rel":"","context":"In &quot;Apple&quot;","block_context":{"text":"Apple","link":"https:\/\/rud.is\/b\/category\/apple\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/09\/rswitch-1.5.0-rstudio-server.png?resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/09\/rswitch-1.5.0-rstudio-server.png?resize=350%2C200 1x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/09\/rswitch-1.5.0-rstudio-server.png?resize=525%2C300 1.5x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/09\/rswitch-1.5.0-rstudio-server.png?resize=700%2C400 2x"},"classes":[]},{"id":5031,"url":"https:\/\/rud.is\/b\/2017\/02\/14\/spelunking-xhrs-xmlhttprequests-with-splashr\/","url_meta":{"origin":4490,"position":1},"title":"Spelunking XHRs (XMLHttpRequests) with splashr","author":"hrbrmstr","date":"2017-02-14","format":false,"excerpt":"splashr has gained some new functionality since the introductory post. First, there's a whole new Docker image for it that embeds a local web server. Why? The main request for it was to enable rendering of htmlwidgets: But if you use the new Docker image and the add_tempdir=TRUE parameter it\u2026","rel":"","context":"In &quot;R&quot;","block_context":{"text":"R","link":"https:\/\/rud.is\/b\/category\/r\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":5004,"url":"https:\/\/rud.is\/b\/2017\/02\/09\/diving-into-dynamic-website-content-with-splashr\/","url_meta":{"origin":4490,"position":2},"title":"Diving Into Dynamic Website Content with splashr","author":"hrbrmstr","date":"2017-02-09","format":false,"excerpt":"If you do enough web scraping, you'll eventually hit a wall that the trusty httr verbs (that sit beneath rvest) cannot really overcome: dynamically created content (via javascript) on a site. If the site was nice enough to use XHR requests to load the dynamic content, you can generally still\u2026","rel":"","context":"In &quot;R&quot;","block_context":{"text":"R","link":"https:\/\/rud.is\/b\/category\/r\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":5796,"url":"https:\/\/rud.is\/b\/2017\/04\/07\/r%e2%81%b6-rstudio-server-client-make-an-app-for-that\/","url_meta":{"origin":4490,"position":3},"title":"R\u2076 \u2014 RStudio Server Client? Make An App For That!","author":"hrbrmstr","date":"2017-04-07","format":false,"excerpt":"RStudio is a great way to work through analyses tasks, and I suspect most folks use the \"desktop\" version of the product on their local workstations. The fine folks at RStudio also make a server version (the codebase for RStudio is able to generate server or desktop and they are\u2026","rel":"","context":"In &quot;R&quot;","block_context":{"text":"R","link":"https:\/\/rud.is\/b\/category\/r\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":3469,"url":"https:\/\/rud.is\/b\/2015\/06\/19\/do-something-nifffty-with-r\/","url_meta":{"origin":4490,"position":4},"title":"DO Something Nifffty with R","author":"hrbrmstr","date":"2015-06-19","format":false,"excerpt":"@briandconnelly (of [pushoverr](http:\/\/crantastic.org\/authors\/4002) fame) made a super-cool post about [connecting R](http:\/\/bconnelly.net\/2015\/06\/connecting-r-to-everything-with-ifttt\/) to @IFTTT via IFTTT's \"Maker\" channel. The IFTTT Maker interface to receive events is fairly straightforward and Brian's code worked flawlessly, so it was easy to tweak a bit and [wrap into a package](https:\/\/github.com\/hrbrmstr\/nifffty). To get started, you can\u2026","rel":"","context":"In &quot;Apple Watch&quot;","block_context":{"text":"Apple Watch","link":"https:\/\/rud.is\/b\/category\/apple-watch\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":5565,"url":"https:\/\/rud.is\/b\/2017\/04\/01\/r%e2%81%b4-snow-day-facets\/","url_meta":{"origin":4490,"position":5},"title":"R\u2076 \u2014 Snow Day Facets","author":"hrbrmstr","date":"2017-04-01","format":false,"excerpt":"Back in 2014 I blogged about first snowfall dates for a given U.S. state. It's April 1, 2017 and we're slated to get 12-18\" of snow up here in Maine and @mrshrbrmstr asked how often this \u2014\u00a0snow in May \u2014 has occurred near us. As with all of these \"R\u2076\u2026","rel":"","context":"In &quot;ggplot&quot;","block_context":{"text":"ggplot","link":"https:\/\/rud.is\/b\/category\/ggplot\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/04\/Cursor_and___projects_snowfirst_-_master_-_RStudio.png?fit=1200%2C1098&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/04\/Cursor_and___projects_snowfirst_-_master_-_RStudio.png?fit=1200%2C1098&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/04\/Cursor_and___projects_snowfirst_-_master_-_RStudio.png?fit=1200%2C1098&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/04\/Cursor_and___projects_snowfirst_-_master_-_RStudio.png?fit=1200%2C1098&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/04\/Cursor_and___projects_snowfirst_-_master_-_RStudio.png?fit=1200%2C1098&ssl=1&resize=1050%2C600 3x"},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/4490","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/comments?post=4490"}],"version-history":[{"count":0,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/4490\/revisions"}],"wp:attachment":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/media?parent=4490"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/categories?post=4490"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/tags?post=4490"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}