<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/when-a-fire-starts-to-burn-fiery-1-0-released/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="http://www.data-imaginist.com/2017/When-a-fire-starts-to-burn/"> Data Imaginist - R posts</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p><img align="right" src="https://i0.wp.com/www.data-imaginist.com/assets/images/fiery_f.jpeg?w=456" data-recalc-dims="1"></p>
<p>I’m pleased to announce that <code>fiery</code> has been updated to version 1.0 and is now<br>
available on CRAN. As the version bump suggests, this is a rather major update<br>
to the package, fixing and improving upon the framework based on my experience<br>
with it, as well as introducing a number of breaking changes. Below, I will go<br>
through the major points of the update, but also give an overview of the<br>
framework itself, as I did not have this blog when it was first released and<br>
information on the framework is thus scarce on the internet (except this<br>
<a rel="nofollow" href="https://rud.is/b/2016/07/05/a-simple-prediction-web-service-using-the-new-firery-package/" target="_blank">nice little post</a><br>
by Bob Rudis).</p>
<h2>Significant changes in v1.0</h2>
<p>The new version of <code>fiery</code> introduces both small and large changes. I’ll start<br>
by listing the breaking changes that one should be aware of in existing <code>fiery</code><br>
servers, and then continue to describe other major changes.</p>
<h3>Embracing reqres <em>BREAKING</em></h3>
<p>My <code>reqres</code> package was<br>
<a rel="nofollow" href="http://www.data-imaginist.com/2017/Introducing-reqres/" target="_blank">recently released</a> and has been<br>
adopted by <code>fiery</code> as the interface for working with HTTP messaging. I have been<br>
a bit torn on whether to build <code>reqres</code> into <code>fiery</code> or simply let<br>
<a rel="nofollow" href="https://github.com/thomasp85/routr" target="_blank"><code>routr</code></a> use it internally, but in the end<br>
the benefits of a more powerful interface to HTTP requests and responses far<br>
outweighed the added dependency and breaking change.</p>
<p>The change means that everywhere a request object is handed on to an event<br>
handler (e.g. handlers listening to the <code>request</code> event) it is no longer passing<br>
a rook environment but a <code>Request</code> object. The easiest fix in existing code is<br>
to simply extract the rook environment from the <code>Request</code> object using the<br>
<code>origin</code> field (this, of course, will not allow you to experience the joy of<br>
<code>reqres</code>).</p>
<p>The change to <code>reqres</code> also brings other, smaller, changes to the code base.<br>
<code>header</code> event handlers are now expected to return either <code>TRUE</code> or <code>FALSE</code> to<br>
indicate whether to proceed or terminate, respectively. Prior to v1.0 they were<br>
expected to return either <code>NULL</code> or a rook complaint list response, but as<br>
responses are now linked to requests, returning them does not make sense. In the<br>
same vein, the return values of <code>request</code> event handlers are ignored and the<br>
response is not passed to <code>after-request</code> event handlers as the response can be<br>
extracted directly from the request.</p>
<h3>Arguments from <em>before-request</em> and <em>before-message</em> event handlers <em>BREAKING</em></h3>
<p>The <code>before-request</code> and <code>before-message</code> events are fired prior to the actual<br>
HTTP request and WebSocket message handling. The return values from any handler<br>
is passed on as arguments to the <code>request</code> and <code>message</code> handlers respectively<br>
and these events can thus be used to inject data into the main request and<br>
message handling. Prior to v1.0 these values were passed in directly as named<br>
arguments, but will now be passed in as a list in the <code>arg_list</code> argument. This<br>
is much easier and consistent to work with. An example of the change is:</p>
<figure>
<pre><code data-lang="r"><span># Old interface
</span><span>app</span><span> </span><span>&lt;-</span><span> </span><span>Fire</span><span>$</span><span>new</span><span>()</span><span>
</span><span>app</span><span>$</span><span>on</span><span>(</span><span>'before-request'</span><span>,</span><span> </span><span>function</span><span>(</span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>list</span><span>(</span><span>arg1</span><span> </span><span>=</span><span> </span><span>'Hello'</span><span>,</span><span> </span><span>arg2</span><span> </span><span>=</span><span> </span><span>'World'</span><span>)</span><span>
</span><span>})</span><span>
</span><span>app</span><span>$</span><span>on</span><span>(</span><span>'request'</span><span>,</span><span> </span><span>function</span><span>(</span><span>arg1</span><span>,</span><span> </span><span>arg2</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>message</span><span>(</span><span>arg1</span><span>,</span><span> </span><span>' '</span><span>,</span><span> </span><span>arg2</span><span>)</span><span>
</span><span>})</span><span>
</span><span># New interface
</span><span>app</span><span> </span><span>&lt;-</span><span> </span><span>Fire</span><span>$</span><span>new</span><span>()</span><span>
</span><span>app</span><span>$</span><span>on</span><span>(</span><span>'before-request'</span><span>,</span><span> </span><span>function</span><span>(</span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>list</span><span>(</span><span>arg1</span><span> </span><span>=</span><span> </span><span>'Hello'</span><span>,</span><span> </span><span>arg2</span><span> </span><span>=</span><span> </span><span>'World'</span><span>)</span><span>
</span><span>})</span><span>
</span><span>app</span><span>$</span><span>on</span><span>(</span><span>'request'</span><span>,</span><span> </span><span>function</span><span>(</span><span>arg_list</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>message</span><span>(</span><span>arg_list</span><span>$</span><span>arg1</span><span>,</span><span> </span><span>' '</span><span>,</span><span> </span><span>arg_list</span><span>$</span><span>arg2</span><span>)</span><span>
</span><span>})</span></code></pre>
</figure>
<p>As can be seen the code ends up being a bit more verbose, but the argument list<br>
will be much more predictable.</p>
<h3>Embracing snake_case <em>BREAKING</em></h3>
<p>When I first started developing <code>fiery</code> I was young and confused<br>
(<img alt="😜" src="https://s.w.org/images/core/emoji/2/72x72/1f61c.png" class="wp-smiley">). Bottom line I don’t think my<br>
naming scheme was very elegant. While consistent (snake_case for methods and<br>
camelCase for fields), this mix is a bit foreign and I’ve decided to use this<br>
major release to clean up in the naming and use snake_case consistently<br>
throughout <code>fiery</code>. This has the effect of renaming the <code>triggerDir</code> field to<br>
<code>trigger_dir</code> and <code>refreshRate</code> to <code>refresh_rate</code>. Furthermore this change is<br>
taken to its conclusion by also changing the plugin interface and require<br>
plugins to expose an <code>on_attach()</code> method rather than an <code>onAttach()</code> method.</p>
<h3>Keeping the event cycle in non-blocking mode</h3>
<p><code>fiery</code> supports running the server in both a blocking and a non-blocking way<br>
(that is, whether control should be returned to the user after the server is<br>
started, or not). Before v1.0 the two modes were not equal in their life cycle<br>
events as only the blocking server had support for <code>cycle-start</code> and <code>cycle-end</code><br>
events as well as handling of timed, delayed, and async evaluation. This has<br>
changed and the lifetime of an app running in the two different modes are now<br>
the same. To achieve this <code>fiery</code> uses the<br>
<a rel="nofollow" href="https://github.com/r-lib/later" target="_blank"><code>later</code></a> package to continually schedule cycle<br>
evaluation for execution. This means that no matter the timing, cycles will only<br>
be executed if the R process is idle, and it also has the slight inconvenience<br>
of not allowing to stop a server as part of a cycle event (Bug report here:<br>
<a rel="nofollow" href="https://github.com/rstudio/httpuv/issues/78" target="_blank">https://github.com/rstudio/httpuv/issues/78</a>). Parallel to the refresh rate of<br>
a blocking server, the refresh rate of a non-blocking server can be set using<br>
the <code>refresh_rate_nb</code> field. By default it is longer than that of a blocking<br>
server, to give the R process more room to receive instructions from the<br>
console.</p>
<h3>Mounting a server</h3>
<p>With v1.0 it is now possible to specify the root of a <code>fiery</code> server. The root<br>
is the part of the URL path that is stripped from the path before sending<br>
requests on to the handler. This means that it is possible to create sub-app in<br>
<code>fiery</code> that do not care at which location they are run. If e.g. the root is set<br>
to <code>/demo/app</code> then requests made for <code>/demo/app/...</code> will look like <code>/...</code><br>
internally, and switching the location of the app does not require any change in<br>
the underlying app logic or routing. The root defaults to <code>''</code> (nothing), but<br>
can be changed with the <code>root</code> field.</p>
<h3>Package documentation</h3>
<p>Documentation can never by too good. The state of affairs for documenting<br>
classes based on reference semantics is not perfect in R, and I still struggle<br>
with the best setup. Still, the current iteration of the documentation is a vast<br>
improvement, compared to the previous release. Notable changes include separate<br>
entries for documentation of events and plugins.</p>
<h3>Grab bag</h3>
<p>The host and port can now be set during construction using the <code>host</code> and <code>port</code><br>
arguments in <code>Fire$new()</code>. <code>Fire</code> objects now has a print method, making them<br>
much nicer to look at. The host, port, and root is now advertised when a server<br>
starts. WebSocket connections can now be closed from the server using the<br>
<code>close_ws_con</code> method.</p>
<h2>A Fiery Overview</h2>
<p>As promised in the beginning, I’ll end with giving an overview of how <code>fiery</code> is<br>
used. I’ll do this by updating Bob’s prediction server to the bright future where<br>
<code>routr</code> and <code>reqres</code> makes life easy for you:</p>
<p>We’ll start by making our fancy AI-machine-learning model of linear<br>
regressiveness:</p>
<figure>
<pre><code data-lang="r"><span>set.seed</span><span>(</span><span>1492</span><span>)</span><span>
</span><span>x</span><span> </span><span>&lt;-</span><span> </span><span>rnorm</span><span>(</span><span>15</span><span>)</span><span>
</span><span>y</span><span> </span><span>&lt;-</span><span> </span><span>x</span><span> </span><span>+</span><span> </span><span>rnorm</span><span>(</span><span>15</span><span>)</span><span>
</span><span>fit</span><span> </span><span>&lt;-</span><span> </span><span>lm</span><span>(</span><span>y</span><span> </span><span>~</span><span> </span><span>x</span><span>)</span><span>
</span><span>saveRDS</span><span>(</span><span>fit</span><span>,</span><span> </span><span>&quot;model.rds&quot;</span><span>)</span></code></pre>
</figure>
<p>With this at our disposable, we can begin to build up our app:</p>
<figure>
<pre><code data-lang="r"><span>library</span><span>(</span><span>fiery</span><span>)</span><span>
</span><span>library</span><span>(</span><span>routr</span><span>)</span><span>
</span><span>app</span><span> </span><span>&lt;-</span><span> </span><span>Fire</span><span>$</span><span>new</span><span>()</span><span>
</span><span># When the app starts, we'll load the model we saved. Instead of
# polluting our namespace we'll use the internal data store
</span><span>
</span><span>app</span><span>$</span><span>on</span><span>(</span><span>'start'</span><span>,</span><span> </span><span>function</span><span>(</span><span>server</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>server</span><span>$</span><span>set_data</span><span>(</span><span>'model'</span><span>,</span><span> </span><span>readRDS</span><span>(</span><span>'model.rds'</span><span>))</span><span>
</span><span>message</span><span>(</span><span>'Model loaded'</span><span>)</span><span>
</span><span>})</span><span>
</span><span># Just for show off, we'll make it so that the model is atomatically
# passed on to the request handlers
</span><span>
</span><span>app</span><span>$</span><span>on</span><span>(</span><span>'before-request'</span><span>,</span><span> </span><span>function</span><span>(</span><span>server</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>list</span><span>(</span><span>model</span><span> </span><span>=</span><span> </span><span>server</span><span>$</span><span>get_data</span><span>(</span><span>'model'</span><span>))</span><span>
</span><span>})</span><span>
</span><span># Now comes the biggest deviation. We'll use routr to define our request
# logic, as this is much nicer
</span><span>router</span><span> </span><span>&lt;-</span><span> </span><span>RouteStack</span><span>$</span><span>new</span><span>()</span><span>
</span><span>route</span><span> </span><span>&lt;-</span><span> </span><span>Route</span><span>$</span><span>new</span><span>()</span><span>
</span><span>router</span><span>$</span><span>add_route</span><span>(</span><span>route</span><span>,</span><span> </span><span>'main'</span><span>)</span><span>
</span><span># We start with a catch-all route that provides a welcoming html page
</span><span>route</span><span>$</span><span>add_handler</span><span>(</span><span>'get'</span><span>,</span><span> </span><span>'*'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>response</span><span>$</span><span>type</span><span> </span><span>&lt;-</span><span> </span><span>'html'</span><span>
</span><span>response</span><span>$</span><span>status</span><span> </span><span>&lt;-</span><span> </span><span>200L</span><span>
</span><span>response</span><span>$</span><span>body</span><span> </span><span>&lt;-</span><span> </span><span>'<h1>All your AI are belong to us</h1>'</span><span>
</span><span>TRUE</span><span>
</span><span>})</span><span>
</span><span># Then on to the /info route
</span><span>route</span><span>$</span><span>add_handler</span><span>(</span><span>'get'</span><span>,</span><span> </span><span>'/info'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>response</span><span>$</span><span>status</span><span> </span><span>&lt;-</span><span> </span><span>200L</span><span>
</span><span>response</span><span>$</span><span>body</span><span> </span><span>&lt;-</span><span> </span><span>structure</span><span>(</span><span>R.Version</span><span>(),</span><span> </span><span>class</span><span> </span><span>=</span><span> </span><span>'list'</span><span>)</span><span>
</span><span>response</span><span>$</span><span>format</span><span>(</span><span>json</span><span> </span><span>=</span><span> </span><span>reqres</span><span>::</span><span>format_json</span><span>())</span><span>
</span><span>TRUE</span><span>
</span><span>})</span><span>
</span><span># Lastly we add the /predict route
</span><span>route</span><span>$</span><span>add_handler</span><span>(</span><span>'get'</span><span>,</span><span> </span><span>'/predict'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>arg_list</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>response</span><span>$</span><span>body</span><span> </span><span>&lt;-</span><span> </span><span>predict</span><span>(</span><span>
</span><span>arg_list</span><span>$</span><span>model</span><span>,</span><span> </span><span>data.frame</span><span>(</span><span>x</span><span>=</span><span>as.numeric</span><span>(</span><span>request</span><span>$</span><span>query</span><span>$</span><span>val</span><span>)),</span><span>
</span><span>se.fit</span><span> </span><span>=</span><span> </span><span>TRUE</span><span>
</span><span>)</span><span>
</span><span>response</span><span>$</span><span>status</span><span> </span><span>&lt;-</span><span> </span><span>200L</span><span>
</span><span>response</span><span>$</span><span>format</span><span>(</span><span>json</span><span> </span><span>=</span><span> </span><span>reqres</span><span>::</span><span>format_json</span><span>())</span><span>
</span><span>TRUE</span><span>
</span><span>})</span><span>
</span><span># And just to show off reqres file handling, we'll add a route # for getting a model plot
</span><span>route</span><span>$</span><span>add_handler</span><span>(</span><span>'get'</span><span>,</span><span> </span><span>'/plot'</span><span>,</span><span> </span><span>function</span><span>(</span><span>request</span><span>,</span><span> </span><span>response</span><span>,</span><span> </span><span>keys</span><span>,</span><span> </span><span>arg_list</span><span>,</span><span> </span><span>...</span><span>)</span><span> </span><span>{</span><span>
</span><span>f_path</span><span> </span><span>&lt;-</span><span> </span><span>tempfile</span><span>(</span><span>fileext</span><span> </span><span>=</span><span> </span><span>'.png'</span><span>)</span><span>
</span><span>png</span><span>(</span><span>f_path</span><span>)</span><span>
</span><span>plot</span><span>(</span><span>arg_list</span><span>$</span><span>model</span><span>)</span><span>
</span><span>dev.off</span><span>()</span><span>
</span><span>response</span><span>$</span><span>status</span><span> </span><span>&lt;-</span><span> </span><span>200L</span><span>
</span><span>response</span><span>$</span><span>file</span><span> </span><span>&lt;-</span><span> </span><span>f_path</span><span>
</span><span>TRUE</span><span>
</span><span>})</span><span>
</span><span># Finally we attach the router to the fiery server
</span><span>app</span><span>$</span><span>attach</span><span>(</span><span>router</span><span>)</span><span>
</span><span>app</span><span>$</span><span>ignite</span><span>(</span><span>block</span><span> </span><span>=</span><span> </span><span>FALSE</span><span>)</span></code></pre>
</figure>
<figure>
<pre><code data-lang="text">## Fire started at 127.0.0.1:8080</code></pre>
</figure>
<figure>
<pre><code data-lang="text">## Model loaded</code></pre>
</figure>
<p>As can be seen, <code>routr</code> makes the request logic nice and compartmentalized,<br>
while <code>reqres</code> makes it easy to work with HTTP messages. What is less apparent is<br>
the work that <code>fiery</code> is doing underneath, but that is exactly the point. While<br>
it is possible to use a lot of the advanced features in <code>fiery</code>, you don’t have<br>
to – often it is as simple as building up a router and attaching it to a <code>fiery</code><br>
instance. Even WebSocket messaging can be offloaded to the router if you so<br>
wish.</p>
<p>Of course a simple prediction service is easy to build up in most frameworks –<br>
it is the To-Do app of data science web server tutorials. I hope to get the time<br>
to create some more fully fledged example apps soon. Next up in the <code>fiery</code><br>
stack pipeline is getting <code>routr</code> on CRAN as well and then begin working on some<br>
of the plugins that will facilitate security, authentication, data storage, etc.</p>
	<p>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="http://www.data-imaginist.com/2017/When-a-fire-starts-to-burn/"> Data Imaginist - R posts</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a rel="nofollow" href="https://www.r-project.org/" title="The R Project for Statistical Computing">R</a> news and <a rel="nofollow" href="https://www.r-bloggers.com/search/tutorial" title="R tutorials">tutorials</a> on topics such as: <a rel="nofollow" href="https://www.r-bloggers.com/search/data%20science" title="Data science">Data science</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Big%20Data" title="Big Data">Big Data, <a rel="nofollow" href="https://www.r-users.com/" title="R jobs">R jobs</a>, visualization (<a rel="nofollow" href="https://www.r-bloggers.com/search/ggplot2" title="ggplot and ggplot2 tutorials">ggplot2</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/boxplot" title="Boxplots using lattice and ggplot2 tutorials">Boxplots</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/map" title="Maps and gis">maps</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/animation" title="Animation in R">animation</a>), programming (<a rel="nofollow" href="https://www.r-bloggers.com/search/RStudio" title="RStudio IDE for R">RStudio</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/sweave" title="Sweave and literate programming">Sweave</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/LaTeX" title="LaTeX in R">LaTeX</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/SQL" title="SQL and databases">SQL</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/eclipse" title="Eclipse IDE for R">Eclipse</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/git" title="git and github, Version Control System">git</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/hadoop" title="Large data in R using Hadoop">hadoop</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/Web+Scraping" title="Web Scraping of google, facebook, yahoo, twitter and more using R">Web Scraping</a>) statistics (<a rel="nofollow" href="https://www.r-bloggers.com/search/regression" title="Regressions and ANOVA analysis tutorials">regression</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/PCA" title="principal component analysis tutorial">PCA</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/time+series" title="Time series">time series</a>, <a rel="nofollow" href="https://www.r-bloggers.com/search/trading" title="finance trading">trading</a>) and more...
<img width="1" alt="" src="http://feeds.feedburner.com/~r/RBloggers/~4/u2xX2AHoGcA" height="1"></a></div>