

{"id":7579,"date":"2017-12-17T15:23:30","date_gmt":"2017-12-17T20:23:30","guid":{"rendered":"https:\/\/rud.is\/b\/?p=7579"},"modified":"2018-03-10T08:01:08","modified_gmt":"2018-03-10T13:01:08","slug":"mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions","status":"publish","type":"post","link":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/","title":{"rendered":"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions"},"content":{"rendered":"<p>I know some folks had a bit of fun with the previous post since it exposed the fact that I left out unique MQTT client id generation from the initial 0.1.0 release of the in-development package (client ids <a href=\"https:\/\/www.ibm.com\/support\/knowledgecenter\/en\/SSFKSJ_7.1.0\/com.ibm.mq.doc\/tt60310_.htm\">need to be unique<\/a>).<\/p>\n<p>There have been some serious improvements since said post and I thought a (hopefully not-too-frequent) blog-journal of the development of this particular package might be interesting\/useful to some folks, especially since I&#8217;m delving into some not-oft-blogged (anywhere) topics as I use some new tricks in this particular package.<\/p>\n<h2>Thank The Great Maker for C++<\/h2>\n<p>I&#8217;m comfortable and not-too-shabby at wrapping C\/C++ things with an R bow and I felt quite daft seeing <a href=\"https:\/\/github.com\/eclipse\/mosquitto\/blob\/master\/lib\/cpp\/mosquittopp.h\">this<\/a> after I had started banging on the <code>mosquitto<\/code> C interface. Yep, that&#8217;s right: it has a C++ interface. It&#8217;s waaaaay easier (in my experience) bridging C++ libraries since Dirk\/Romain&#8217;s (et al, as I know there are <a href=\"https:\/\/github.com\/RcppCore\/Rcpp\/graphs\/contributors\">many worker hands<\/a> involved as well) <code>Rcpp<\/code> has <em>so many tools<\/em> available to do that very thing.<\/p>\n<blockquote><p>\n  <em>As an aside, if you do any work with Rcpp or want to start doing work in Rcpp, Masaki E. Tsuda&#8217;s <a href=\"https:\/\/teuder.github.io\/rcpp4everyone_en\/\">Rcpp for Everyone<\/a> is an <strong>excellent<\/strong> tome.<\/em>\n<\/p><\/blockquote>\n<p>I hadn&#8217;t used <a href=\"http:\/\/jamescurran.co.nz\/2017\/01\/eg-html\/\">Rcpp Modules<\/a> before (that link goes to a succinct but very helpful post by James Curran) but they make exposing C++ library functionality even easier than I had experienced before. So easy, in fact, that it made it possible to whip out an alpha version of a &#8220;domain specific language&#8221; (or a pipe-able, customized API &#8212; however you want to frame these things in your head) for the package. But, I&#8217;m getting ahead of myself.<\/p>\n<p>The <code>mosquittopp<\/code> class in the <code>mosqpp<\/code> namespace is much like the third bowl of porridge: just right. It&#8217;s neither too low-level nor too high-level and it was well thought out enough that it only required a bit of tweaking to use as an Rcpp Module.<\/p>\n<p>First there are more than a few <code>char *<\/code> parameters that needed to be <code>std::string<\/code>s. So, something like:<\/p>\n<pre id=\"mosdevlog0101\"><code class=\"language-cpp\">int username_pw_set(const char *username, const char *password);<\/code><\/pre>\n<p>becomes:<\/p>\n<pre id=\"mosdevlog0102\"><code class=\"language-cpp\">int username_pw_set(std::string username, std::string password);<\/code><\/pre>\n<p>in our custom wrapper class.<\/p>\n<p>Since the whole point of the <code>mqtt<\/code> package is to work in R vs C[++] or any other language, the callbacks &#8212; the functions that do the work when <code>message<\/code>, <code>publish<\/code>, <code>subscribe<\/code>, etc. events are triggered &#8212; need to be in R. I wanted to have some default callbacks during the testing phase and they&#8217;re really straightforward to setup in <code>Rcpp<\/code>:<\/p>\n<pre id=\"mosdevlog0103\"><code class=\"language-cpp\">Rcpp::Environment pkg_env = Rcpp::Environment::namespace_env(&quot;mqtt&quot;);\r\n\r\nRcpp::Function ccb = pkg_env[&quot;.mqtt_connect_cb&quot;];\r\nRcpp::Function dcb = pkg_env[&quot;.mqtt_disconnect_cb&quot;];\r\nRcpp::Function pcb = pkg_env[&quot;.mqtt_publish_cb&quot;];\r\nRcpp::Function mcb = pkg_env[&quot;.mqtt_message_cb&quot;];\r\nRcpp::Function scb = pkg_env[&quot;.mqtt_subscribe_cb&quot;];\r\nRcpp::Function ucb = pkg_env[&quot;.mqtt_unsubscribe_cb&quot;];\r\nRcpp::Function lcb = pkg_env[&quot;.mqtt_log_cb&quot;];\r\nRcpp::Function ecb = pkg_env[&quot;.mqtt_error_cb&quot;];<\/code><\/pre>\n<p>The handy thing about that approach is you don&#8217;t need to export the functions (it works like the <code>:::<\/code> does).<\/p>\n<p>But the kicker is the Rcpp Module syntactic sugar:<\/p>\n<pre id=\"mosdevlog0104\"><code class=\"language-cpp\">RCPP_MODULE(MQTT) {\r\n\r\n  using namespace Rcpp;\r\n\r\n  class_&lt;mqtt_r&gt;(&quot;mqtt_r&quot;)\r\n    .constructor&lt;std::string, std::string, int&gt;(&quot;id\/host\/port constructor&quot;)\r\n    .constructor&lt;std::string, std::string, int, std::string, std::string&gt;(&quot;id\/host\/port\/user\/pass constructor&quot;)\r\n    .constructor&lt;std::string, std::string, int, Rcpp::Function, Rcpp::Function, Rcpp::Function&gt;(&quot;id\/host\/post\/con\/mess\/discon constructor&quot;)\r\n    .method(&quot;connect&quot;, &amp;mqtt_r::connect)\r\n    .method(&quot;disconnect&quot;, &amp;mqtt_r::disconnect)\r\n    .method(&quot;reconnect&quot;, &amp;mqtt_r::reconnect)\r\n    .method(&quot;username_pw_set&quot;, &amp;mqtt_r::username_pw_set)\r\n    .method(&quot;loop_start&quot;, &amp;mqtt_r::loop_start)\r\n    .method(&quot;loop_stop&quot;, &amp;mqtt_r::loop_stop)\r\n    .method(&quot;loop&quot;, &amp;mqtt_r::loop)\r\n    .method(&quot;publish_raw&quot;, &amp;mqtt_r::publish_raw)\r\n    .method(&quot;publish_chr&quot;, &amp;mqtt_r::publish_chr)\r\n    .method(&quot;subscribe&quot;, &amp;mqtt_r::subscribe)\r\n    .method(&quot;unsubscribe&quot;, &amp;mqtt_r::unsubscribe)\r\n    .method(&quot;set_connection_cb&quot;, &amp;mqtt_r::set_connection_cb)\r\n    .method(&quot;set_discconn_cb&quot;, &amp;mqtt_r::set_discconn_cb)\r\n    .method(&quot;set_publish_cb&quot;, &amp;mqtt_r::set_publish_cb)\r\n    .method(&quot;set_message_cb&quot;, &amp;mqtt_r::set_message_cb)\r\n    .method(&quot;set_subscribe_cb&quot;, &amp;mqtt_r::set_subscribe_cb)\r\n    .method(&quot;set_unsubscribe_cb&quot;, &amp;mqtt_r::set_unsubscribe_cb)\r\n    .method(&quot;set_log_cb&quot;, &amp;mqtt_r::set_log_cb)\r\n    .method(&quot;set_error_cb&quot;, &amp;mqtt_r::set_error_cb)\r\n   ;\r\n\r\n}<\/code><\/pre>\n<p>That, combined with <code>RcppModules: MQTT<\/code> in the <code>DESCRIPTION<\/code> file and a <code>MQTT &lt;- Rcpp::Module(\"MQTT\")<\/code> just above where you&#8217;d put an <code>.onLoad<\/code> handler means you can do something like (internally, since it&#8217;s not exported):<\/p>\n<pre id=\"mosdevlog0105\"><code class=\"language-r\">mqtt_obj &lt;- MQTT$mqtt_r\r\n\r\nmqtt_conn_obj &lt;- new(mqtt_obj, &quot;unique_client_id&quot;, &quot;test.mosquitto.org&quot;, 1883L)<\/code><\/pre>\n<p>and have access to each of those methods right from R (e.g. <code>mqtt_conn_obj$subscribe(0, \"topic\", 0)<\/code>).<\/p>\n<p>If you&#8217;re careful with your C++ class code, you&#8217;ll be able to breeze through exposing functionality.<\/p>\n<p>Because of the existence of Rcpp Modules, I was able to do what follows in the next section in near record time.<\/p>\n<h2>&#8220;The stump of a <code>%&gt;%<\/code> he held tight in his teeth&#8221;<\/h2>\n<p>I felt compelled to get a Christmas reference in the post and it&#8217;s relevant to this section. I like <code>%&gt;%<\/code>, recommend the use of <code>%&gt;%<\/code> and use <code>%&gt;%<\/code> in my own day-to-day R coding (it&#8217;s even creeping into internal package code, though I still try not to do that). I knew I wanted to expose a certain way of approaching MQTT workflows in this <code>mqtt<\/code> package and that meant coming up with an initial &#8212; but far from complete &#8212; mini-language or pipe-able API for it. Here&#8217;s the current thinking\/implementation:<\/p>\n<ul>\n<li><strong>Setup connection parameters with <code>mqtt_broker()<\/code><\/strong>. For now, it takes some parameters, but there is a <a href=\"https:\/\/github.com\/mqtt\/mqtt.github.io\/wiki\/URI-Scheme\">URI scheme for MQTT<\/a> so I want to be able to support that as well at some point.<\/li>\n<li><strong>Support authentication with <code>mqtt_username_pw()<\/code><\/strong>. There will also be a function for dealing with certificates and other security-ish features which look similar to this.<\/li>\n<li><strong>Make it dead-easy to subscribe to topics and associate callbacks with <code>mqtt_subscribe()<\/code><\/strong> (more on this below)<\/li>\n<li><strong>Support an &#8220;until you kill it&#8221; workflow with <code>mqtt_run()<\/code><\/strong> that loops either forever or for a certain number of iterations<\/li>\n<li><strong>Support user-controlled iterations with <code>mqtt_begin()<\/code>, <code>mqtt_loop()<\/code> and <code>mqtt_end()<\/code><\/strong>. An example (in a bit) should help explain this further, but this one is likely to be especially useful in a Shiny context.<\/li>\n<\/ul>\n<p>Now, as hopefully came across in the previous post, the heart of MQTT message processing is the <em>callback<\/em> function. You write a function with a contractually defined set of parameters and operate on the values passed in. While we should all likely get into a better habit of using named function objects vs anonymous functions, anonymous functions are <em>super handy<\/em>, and short ones don&#8217;t cause code to get too gnarly. <strong>However<\/strong>, in this new DSL\/API I&#8217;ve cooked up, each topic <code>message<\/code> callback has <strong>six<\/strong> parameters, so that means if you want to use an anonymous function (vs a named one) you have to do something like this in <code>message_subscribe()<\/code>:<\/p>\n<pre id=\"mosdevlog0106\"><code class=\"language-r\">mqtt_subscribe(&quot;sometopic&quot;,  function(id, topic, payload, qos, retain, con) {})<\/code><\/pre>\n<p>That&#8217;s not very succinct, elegant or handy. Since those are three attributes I absolutely ? about most things related to R, I had to do something about it.<\/p>\n<p>Since I&#8217;m highly attached to the <code>~{}<\/code> syntax introduced with <code>purrr<\/code> and now expanding across the Tidyverse, I decided to make a custom version of it for <code>message_subscribe()<\/code>. As a result, the code above can be written as:<\/p>\n<pre id=\"mosdevlog0108\"><code class=\"language-r\">mqtt_subscribe(&quot;sometopic&quot;,  ~{})<\/code><\/pre>\n<p>and, you can reference <code>id<\/code>, <code>topic<\/code>, <code>payload<\/code>, etc inside those brackets without the verbose function declaration.<\/p>\n<p>How is this accomplished? Via:<\/p>\n<pre id=\"mosdevlog0107\"><code class=\"language-r\">as_message_callback &lt;- function(x, env = rlang::caller_env()) {\r\n  rlang::coerce_type(\r\n    x, rlang::friendly_type(&quot;function&quot;),\r\n    closure = { x },\r\n    formula = {\r\n      if (length(x) &gt; 2) rlang::abort(&quot;Can&#039;t convert a two-sided formula to an mqtt message callback function&quot;)\r\n      f &lt;- function() { x }\r\n      formals(f) &lt;- alist(id=, topic=, payload=, qos=, retain=, con=)\r\n      body(f) &lt;- rlang::f_rhs(x)\r\n      f\r\n    }\r\n  )\r\n}<\/code><\/pre>\n<p>It&#8217;s a shortened version of some Tidyverse code that&#8217;s more generic in nature. That <code>as_message_callback()<\/code> function looks to see if you&#8217;ve passed in a <code>~{}<\/code> or a named\/anonymous function. If <code>~{}<\/code> was used, that function builds a function with the contractually obligated\/expected signature, otherwise it shoves in what you gave it.<\/p>\n<p>A code example is worth a thousand words (which is, in fact, the precise number of &#8220;words&#8221; up until this snippet, at least insofar as the WordPress editor counts them):<\/p>\n<pre id=\"mosdevlog0109\"><code class=\"language-r\">library(mqtt)\r\n\r\n# We&#039;re going to subscribe to *three* BBC subtitle feeds at the same time!\r\n#\r\n# We&#039;ll distinguish between them by coloring the topic and text differently.\r\n\r\n# this is a named function object that displays BBC 2&#039;s subtitle feed when it get messages\r\nmoar_bbc &lt;- function(id, topic, payload, qos, retain, con) {\r\n  if (topic == &quot;bbc\/subtitles\/bbc_two_england\/raw&quot;) {\r\n    cat(crayon::cyan(topic), crayon::blue(readBin(payload, &quot;character&quot;)), &quot;\\n&quot;, sep=&quot; &quot;)\r\n  }\r\n}\r\n\r\nmqtt_broker(&quot;makmeunique&quot;, &quot;test.mosquitto.org&quot;, 1883L) %&gt;% # connection info\r\n  \r\n  mqtt_silence(c(&quot;all&quot;)) %&gt;% # silence all the development screen messages\r\n  \r\n  # subscribe to BBC 1&#039;s topic using a fully specified anonyous function\r\n  \r\n  mqtt_subscribe(\r\n    &quot;bbc\/subtitles\/bbc_one_london\/raw&quot;,\r\n    function(id, topic, payload, qos, retain, con) { # regular anonymous function\r\n      if (topic == &quot;bbc\/subtitles\/bbc_one_london\/raw&quot;)\r\n        cat(crayon::yellow(topic), crayon::green(readBin(payload, &quot;character&quot;)), &quot;\\n&quot;, sep=&quot; &quot;)\r\n    }) %&gt;%\r\n  \r\n  # as you can see we can pipe-chain as many subscriptions as we like. the package \r\n  # handles the details of calling each of them. This makes it possible to have\r\n  # very focused handlers vs lots of &quot;if\/then\/case_when&quot; impossible-to-read functions.\r\n  \r\n  # Ahh. A tidy, elegant, succinct ~{} function instead\r\n  \r\n  mqtt_subscribe(&quot;bbc\/subtitles\/bbc_news24\/raw&quot;, ~{ # tilde shortcut function (passing in named, pre-known params)\r\n    if (topic == &quot;bbc\/subtitles\/bbc_news24\/raw&quot;)\r\n      cat(crayon::yellow(topic), crayon::red(readBin(payload, &quot;character&quot;)), &quot;\\n&quot;, sep=&quot; &quot;)\r\n  }) %&gt;%\r\n  \r\n  # And, a boring, but -- in the long run, better (IMO) -- named function object\r\n  \r\n  mqtt_subscribe(&quot;bbc\/subtitles\/bbc_two_england\/raw&quot;, moar_bbc) %&gt;% # named function\r\n  \r\n  mqtt_run() -&gt; res # this runs until you Ctrl-C<\/code><\/pre>\n<p><img data-recalc-dims=\"1\" decoding=\"async\" src=\"https:\/\/i0.wp.com\/rud.is\/dl\/multi-bbc.gif?w=510&#038;ssl=1\" alt=\"\" \/><\/p>\n<p>There&#8217;s in-code commentary, so I&#8217;ll refrain from blathering about it more here except for noting there are a <em>staggering amount<\/em> of depressing stories on BBC News and an equally staggering amount of un-hrbrmstr-like language use in BBC One and BBC Two shows. Apologies if any of the GH <code>README.md<\/code> snippets or animated screenshots ever cause offense, as it&#8217;s quite unintentional.<\/p>\n<h2>But you said something about begin\/end\/loop before?<\/h2>\n<p>Quite right! For that we&#8217;ll use a different example.<\/p>\n<p>I came across a topic &#8212; &#8220;<code>sfxrider\/+\/locations<\/code>&#8221; &#8212; on <code>broker.mqttdashboard.com<\/code>. It <em>looks<\/em> like live data from folks who do transportation work for &#8220;<span class=\"removed_link\" title=\"https:\/\/www.crunchbase.com\/organization\/shadowfax-technologies\">Shadowfax Technologies<\/span>&#8221; (which is a crowd-sourced transportation\/logistics provider in India). It publishes the following in the payload:<\/p>\n<pre id=\"mosdevlog0110\"><code class=\"language-bash\">| device:6170774037 | latitude:28.518363 | longitude:77.095753 | timestamp:1513539899000 |\r\n| device:6170774037 | latitude:28.518075 | longitude:77.09555 | timestamp:1513539909000 |\r\n| device:6170774037 | latitude:28.518015 | longitude:77.095488 | timestamp:1513539918000 |\r\n| device:8690150597 | latitude:28.550963 | longitude:77.13432 | timestamp:1513539921000 |\r\n| device:6170774037 | latitude:28.518018 | longitude:77.095492 | timestamp:1513539928000 |\r\n| device:6170774037 | latitude:28.518022 | longitude:77.095495 | timestamp:1513539938000 |\r\n| device:6170774037 | latitude:28.518025 | longitude:77.095505 | timestamp:1513539947000 |\r\n| device:6170774037 | latitude:28.518048 | longitude:77.095527 | timestamp:1513539957000 |\r\n| device:6170774037 | latitude:28.518075 | longitude:77.095573 | timestamp:1513539967000 |\r\n| device:8690150597 | latitude:28.550963 | longitude:77.13432 | timestamp:1513539975000 |\r\n| device:6170774037 | latitude:28.518205 | longitude:77.095603 | timestamp:1513539977000 |\r\n| device:6170774037 | latitude:28.5182 | longitude:77.095587 | timestamp:1513539986000 |\r\n| device:6170774037 | latitude:28.518202 | longitude:77.095578 | timestamp:1513539996000 |\r\n| device:6170774037 | latitude:28.5182 | longitude:77.095578 | timestamp:1513540006000 |\r\n| device:6170774037 | latitude:28.518203 | longitude:77.095577 | timestamp:1513540015000 |\r\n| device:6170774037 | latitude:28.518208 | longitude:77.095577 | timestamp:1513540025000 |<\/code><\/pre>\n<p>Let&#8217;s turn that into proper, usable, JSON (we&#8217;ll just <code>cat()<\/code> it out for this post):<\/p>\n<pre id=\"mosdevlog0111\"><code class=\"language-r\">library(mqtt)\r\nlibrary(purrr)\r\nlibrary(stringi)\r\n\r\n# turn the pipe-separated, colon-delimeted lines into a proper list\r\n.decode_payload &lt;- function(.x) {\r\n  .x &lt;- readBin(.x, &quot;character&quot;)\r\n  .x &lt;- stri_match_all_regex(.x, &quot;([[:alpha:]]+):([[:digit:]\\\\.]+)&quot;)[[1]][,2:3]\r\n  .x &lt;- as.list(setNames(as.numeric(.x[,2]), .x[,1]))\r\n  .x$timestamp &lt;- as.POSIXct(.x$timestamp\/1000, origin=&quot;1970-01-01 00:00:00&quot;)\r\n  .x\r\n}\r\n\r\n# do it safely as the payload in MQTT can be anything\r\ndecode_payload &lt;- purrr::safely(.decode_payload)\r\n\r\n# change the client id\r\nmqtt_broker(&quot;makemeuique&quot;, &quot;broker.mqttdashboard.com&quot;, 1883L) %&gt;%\r\n  mqtt_silence(c(&quot;all&quot;)) %&gt;%\r\n  mqtt_subscribe(&quot;sfxrider\/+\/locations&quot;, ~{\r\n    x &lt;- decode_payload(payload)$result\r\n    if (!is.null(x)) {\r\n      cat(crayon::yellow(jsonlite::toJSON(x, auto_unbox=TRUE), &quot;\\n&quot;, sep=&quot;&quot;))\r\n    }\r\n  }) %&gt;%\r\n  mqtt_run(times = 10000) -&gt; out<\/code><\/pre>\n<p><a href=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/tojson.gif?ssl=1\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"7592\" data-permalink=\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/tojson\/\" data-orig-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/tojson.gif?fit=1713%2C1016&amp;ssl=1\" data-orig-size=\"1713,1016\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"tojson\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/tojson.gif?fit=300%2C178&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/tojson.gif?fit=510%2C302&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/tojson.gif?resize=510%2C302&#038;ssl=1\" alt=\"\" width=\"510\" height=\"302\" class=\"aligncenter size-full wp-image-7592\" \/><\/a><\/p>\n<p>What if you wanted do that one-by-one so you could plot the data live in a Shiny map? Well, we won&#8217;t do <em>that<\/em> in this post, but the user-controlled loop version would look like this:<\/p>\n<pre id=\"mosdevlog0112\"><code class=\"language-r\">mqtt_broker(&quot;makemeuique&quot;, &quot;broker.mqttdashboard.com&quot;, 1883L) %&gt;%\r\n  mqtt_silence(c(&quot;all&quot;)) %&gt;%\r\n  mqtt_subscribe(&quot;sfxrider\/+\/locations&quot;, ~{\r\n    x &lt;- decode_payload(payload)$result\r\n    if (!is.null(x)) {\r\n      cat(crayon::yellow(jsonlite::toJSON(x, auto_unbox=TRUE), &quot;\\n&quot;, sep=&quot;&quot;))\r\n    }\r\n  }) %&gt;%\r\n  mqtt_begin() -&gt; tracker # _begin!! not _run!!\r\n\r\n# call this individually and have the callback update a\r\n# larger scoped variable or Redis or a database. You\r\n# can also just loop like this `for` setup.\r\n\r\nfor (i in 1:25) mqtt_loop(tracker, timeout = 1000)\r\n\r\nmqtt_end(tracker) # this cleans up stuff!<\/code><\/pre>\n<h2>FIN<\/h2>\n<p>Whew. 1,164 words later and I hope I&#8217;ve kept your interest through it all. I&#8217;ve updated the GH repo for the package and also updated the requirements for the package in the README. I&#8217;m also working on a <code>configure<\/code> script (mimicking @opencpu&#8217;s &#8216;anti-conf&#8217; approach) and found Win32 library binaries that should make this easier to get up and running on Windows, so stay tuned for the next installment and don&#8217;t hesitate to jump on board with issues, questions, comments or PRs.<\/p>\n<p>The goal for the next post is to cover reading from either that logistics feed or <a href=\"http:\/\/owntracks.org\/\">OwnTracks<\/a> and dynamically display points on a map with Shiny. Stay tuned!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I know some folks had a bit of fun with the previous post since it exposed the fact that I left out unique MQTT client id generation from the initial 0.1.0 release of the in-development package (client ids need to be unique). There have been some serious improvements since said post and I thought a [&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":[805,91],"tags":[810],"class_list":["post-7579","post","type-post","status-publish","format-standard","hentry","category-mqtt","category-r","tag-post"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions - 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\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions - rud.is\" \/>\n<meta property=\"og:description\" content=\"I know some folks had a bit of fun with the previous post since it exposed the fact that I left out unique MQTT client id generation from the initial 0.1.0 release of the in-development package (client ids need to be unique). There have been some serious improvements since said post and I thought a [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/\" \/>\n<meta property=\"og:site_name\" content=\"rud.is\" \/>\n<meta property=\"article:published_time\" content=\"2017-12-17T20:23:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-03-10T13:01:08+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/rud.is\/dl\/multi-bbc.gif\" \/>\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=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/\"},\"author\":{\"name\":\"hrbrmstr\",\"@id\":\"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886\"},\"headline\":\"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions\",\"datePublished\":\"2017-12-17T20:23:30+00:00\",\"dateModified\":\"2018-03-10T13:01:08+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/\"},\"wordCount\":1361,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886\"},\"image\":{\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/rud.is\/dl\/multi-bbc.gif\",\"keywords\":[\"post\"],\"articleSection\":[\"mqtt\",\"R\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/\",\"url\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/\",\"name\":\"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions - rud.is\",\"isPartOf\":{\"@id\":\"https:\/\/rud.is\/b\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/rud.is\/dl\/multi-bbc.gif\",\"datePublished\":\"2017-12-17T20:23:30+00:00\",\"dateModified\":\"2018-03-10T13:01:08+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#primaryimage\",\"url\":\"https:\/\/rud.is\/dl\/multi-bbc.gif\",\"contentUrl\":\"https:\/\/rud.is\/dl\/multi-bbc.gif\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/rud.is\/b\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions\"}]},{\"@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":"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions - 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\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/","og_locale":"en_US","og_type":"article","og_title":"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions - rud.is","og_description":"I know some folks had a bit of fun with the previous post since it exposed the fact that I left out unique MQTT client id generation from the initial 0.1.0 release of the in-development package (client ids need to be unique). There have been some serious improvements since said post and I thought a [&hellip;]","og_url":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/","og_site_name":"rud.is","article_published_time":"2017-12-17T20:23:30+00:00","article_modified_time":"2018-03-10T13:01:08+00:00","og_image":[{"url":"https:\/\/rud.is\/dl\/multi-bbc.gif","type":"","width":"","height":""}],"author":"hrbrmstr","twitter_card":"summary_large_image","twitter_misc":{"Written by":"hrbrmstr","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#article","isPartOf":{"@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/"},"author":{"name":"hrbrmstr","@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"headline":"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions","datePublished":"2017-12-17T20:23:30+00:00","dateModified":"2018-03-10T13:01:08+00:00","mainEntityOfPage":{"@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/"},"wordCount":1361,"commentCount":1,"publisher":{"@id":"https:\/\/rud.is\/b\/#\/schema\/person\/d7cb7487ab0527447f7fda5c423ff886"},"image":{"@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#primaryimage"},"thumbnailUrl":"https:\/\/rud.is\/dl\/multi-bbc.gif","keywords":["post"],"articleSection":["mqtt","R"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/","url":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/","name":"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions - rud.is","isPartOf":{"@id":"https:\/\/rud.is\/b\/#website"},"primaryImageOfPage":{"@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#primaryimage"},"image":{"@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#primaryimage"},"thumbnailUrl":"https:\/\/rud.is\/dl\/multi-bbc.gif","datePublished":"2017-12-17T20:23:30+00:00","dateModified":"2018-03-10T13:01:08+00:00","breadcrumb":{"@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#primaryimage","url":"https:\/\/rud.is\/dl\/multi-bbc.gif","contentUrl":"https:\/\/rud.is\/dl\/multi-bbc.gif"},{"@type":"BreadcrumbList","@id":"https:\/\/rud.is\/b\/2017\/12\/17\/mqtt-development-log-on-dsls-rcpp-modules-and-custom-formula-functions\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/rud.is\/b\/"},{"@type":"ListItem","position":2,"name":"mqtt Development Log : On DSLs, Rcpp Modules and Custom Formula Functions"}]},{"@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-1Yf","jetpack_likes_enabled":true,"jetpack-related-posts":[{"id":7556,"url":"https:\/\/rud.is\/b\/2017\/12\/14\/inter-operate-with-mqtt-message-brokers-with-r-a-k-a-live-bbc-subtitles\/","url_meta":{"origin":7579,"position":0},"title":"Inter-operate with &#8216;MQTT&#8217; Message Brokers With R (a.k.a. Live! BBC! Subtitles!)","author":"hrbrmstr","date":"2017-12-14","format":false,"excerpt":"Most of us see the internet through the lens of browsers and apps on our laptops, desktops, watches, TVs and mobile devices. These displays are showing us --- for the most part --- content designed for human consumption. Sure, apps handle API interactions, but even most of that communication happens\u2026","rel":"","context":"In &quot;mqtt&quot;","block_context":{"text":"mqtt","link":"https:\/\/rud.is\/b\/category\/mqtt\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/mqttmap-1.png?fit=1200%2C536&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/mqttmap-1.png?fit=1200%2C536&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/mqttmap-1.png?fit=1200%2C536&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/mqttmap-1.png?fit=1200%2C536&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/12\/mqttmap-1.png?fit=1200%2C536&ssl=1&resize=1050%2C600 3x"},"classes":[]},{"id":6491,"url":"https:\/\/rud.is\/b\/2017\/09\/28\/sodd-stackoverflow-driven-development\/","url_meta":{"origin":7579,"position":1},"title":"SODD \u2014 StackOverflow Driven-Development","author":"hrbrmstr","date":"2017-09-28","format":false,"excerpt":"I occasionally hang out on StackOverflow and often use an answer as an opportunity to fill a package void for a particular need. docxtractr and qrencoder are two (of many) packages that were birthed from SO answers. I usually try to answer with inline code first then expand the functionality\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":3298,"url":"https:\/\/rud.is\/b\/2015\/03\/09\/new-r-package-ipapi-ipdomain-geolocation\/","url_meta":{"origin":7579,"position":2},"title":"New R Package &#8211; ipapi (IP\/Domain Geolocation)","author":"hrbrmstr","date":"2015-03-09","format":false,"excerpt":"I noticed that the @rOpenSci folks had an interface to [ip-api.com](http:\/\/ip-api.com\/) on their [ToDo](https:\/\/github.com\/ropensci\/webservices\/wiki\/ToDo) list so I whipped up a small R package to fill said gap. Their IP Geolocation API will take an IPv4, IPv6 or FQDN and kick back a ASN, lat\/lon, address and more. The [ipapi package](https:\/\/github.com\/hrbrmstr\/ipapi)\u2026","rel":"","context":"In &quot;cartography&quot;","block_context":{"text":"cartography","link":"https:\/\/rud.is\/b\/category\/cartography\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":12586,"url":"https:\/\/rud.is\/b\/2020\/01\/01\/writing-frictionless-r-package-wrappers-introduction\/","url_meta":{"origin":7579,"position":3},"title":"Writing Frictionless R Package Wrappers \u2014 Introduction","author":"hrbrmstr","date":"2020-01-01","format":false,"excerpt":"The R language and RStudio IDE are a powerful combination for \"getting stuff done\", and one aspect of R itself that makes it especially useful is the ability to use it with other programming languages via a robust foreign language interface capability1. The term \"foreign language\" refers to another programming\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":4852,"url":"https:\/\/rud.is\/b\/2017\/01\/08\/2017-01-authored-package-updates\/","url_meta":{"origin":7579,"position":4},"title":"2017-01 Authored Package Updates","author":"hrbrmstr","date":"2017-01-08","format":false,"excerpt":"The rest of the month is going to be super-hectic and it's unlikely I'll be able to do any more to help the push to CRAN 10K, so here's a breakdown of CRAN and GitHub new packages & package updates that I felt were worth raising awareness on: epidata I\u2026","rel":"","context":"In &quot;dplyr&quot;","block_context":{"text":"dplyr","link":"https:\/\/rud.is\/b\/category\/dplyr\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/01\/epi2.png?fit=982%2C1200&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/01\/epi2.png?fit=982%2C1200&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/01\/epi2.png?fit=982%2C1200&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2017\/01\/epi2.png?fit=982%2C1200&ssl=1&resize=700%2C400 2x"},"classes":[]},{"id":11775,"url":"https:\/\/rud.is\/b\/2019\/01\/21\/hrbrthemes-0-6-0-on-cran-other-in-development-package-news\/","url_meta":{"origin":7579,"position":5},"title":"hrbrthemes 0.6.0 on CRAN + Other In-Development Package News","author":"hrbrmstr","date":"2019-01-21","format":false,"excerpt":"Version 0.6.0 of the hrbrthemes? package should be hitting a CRAN mirror near you soon. Apart from some general documentation and code cleanup this release includes the dark theme folks have been seeing in blog posts and tweets over the past few months. It's called theme_ft_rc() since it is an\u2026","rel":"","context":"In &quot;maine&quot;","block_context":{"text":"maine","link":"https:\/\/rud.is\/b\/category\/maine\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/01\/nc-idc-coverage.png?fit=1200%2C456&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/01\/nc-idc-coverage.png?fit=1200%2C456&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/01\/nc-idc-coverage.png?fit=1200%2C456&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/01\/nc-idc-coverage.png?fit=1200%2C456&ssl=1&resize=700%2C400 2x, https:\/\/i0.wp.com\/rud.is\/b\/wp-content\/uploads\/2019\/01\/nc-idc-coverage.png?fit=1200%2C456&ssl=1&resize=1050%2C600 3x"},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/7579","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=7579"}],"version-history":[{"count":0,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/posts\/7579\/revisions"}],"wp:attachment":[{"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/media?parent=7579"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/categories?post=7579"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rud.is\/b\/wp-json\/wp\/v2\/tags?post=7579"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}