-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
88 lines (87 loc) · 8.73 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<!DOCTYPE html>
<!-- This file was auto-generated by exmd at 2023-04-04T10:41:09.270Z. Do NOT edit by hand! -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>🧪 Webr + localForage 🫙</title><meta property="og:title" content="🧪 Webr + localForage 🫙">
<meta property="twitter:title" content="🧪 Webr + localForage 🫙">
<meta name="description" content="When you need to keep R stuff around for a bit.">
<meta property="og:description" content="When you need to keep R stuff around for a bit.">
<meta property="twitter:description" content="When you need to keep R stuff around for a bit.">
<meta property="og:site" content="https://rud.is/w/webr-localforage">
<meta property="og:site_name" content="WebR Exeriments">
<meta property="og:image" content="https://rud.is/w/webr-localforage/preview.png">
<meta property="twitter:image" content="https://rud.is/w/webr-localforage/preview.png">
<meta property="og:image:width" content="1014">
<meta property="og:image:height" content="836">
<meta property="og:image:alt" content="example">
<meta property="twitter:site_name" content="@hrbrmstr">
<meta property="twitter:domain" content="rud.is">
<meta property="twitter:card" content="summary_large_image">
<meta property="article:published_time" content="2023-04-04T10:41:09.271Z">
<link rel='apple-touch-icon' sizes='180x180' href='./favicon/apple-touch-icon.png'>
<link rel='icon' type='image/png' sizes='32x32' href='./favicon/favicon-32x32.png'>
<link rel='icon' type='image/png' sizes='16x16' href='./favicon/favicon-16x16.png'>
<link rel='manifest' href='./favicon/site.webmanifest'>
<link href='./src/index.css' rel='stylesheet'>
<link href='./src/components.css' rel='stylesheet'>
<script type='module' src='./src/main.js'></script>
</head>
<body>
<h1>🧪 🕸️ 🫙 WebR + localForage</h1>
<p><status-message id="webr-status" text="WebR Loading…"></status-message></p>
<h2>When you need to keep R stuff around for a bit.</h2>
<p>Experiment hypothesis:</p>
<blockquote>
<p>We can keep actual R objects around for a while thanks to R's <code>[un]serialze()</code> and <a href="https://localforage.github.io/localForage/"><code>localForage</code></a></p>
</blockquote>
<p>Experiment parameters:</p>
<ul>
<li>Webr</li>
<li><span class="pill">New!</span> The ^^ mentioned <code>localForage</code></li>
<li><span class="pill">New!</span> A fairly reusable (but basic) Lit component to display a data frame with selected columns</li>
<li>Lit (web components)</li>
<li>Vite (for building)</li>
</ul>
<hr>
<h2>Fiddling With Local Storage</h2>
<p><action-button id="action" label=""></action-button></p>
<p><simple-message id="msg"></simple-message></p>
<p><data-frame-view id="mtcars2" label="Copy of mtcars (serialized to local storage)"></data-frame-view></p>
<hr>
<h2>If nothing else, you <em>sure</em> are a persistent one, hrbrmstr</h2>
<p>Aye, that I am!</p>
<p>I'm willing to bet you <em>(almost)</em> never use R's super cool <a href="%5BremoveLabel%5D(https://rdrr.io/r/base/serialize.html)"><code>serialize()</code></a> function (directly). That function takes an R object and transforms it into a format that can be persisted outside R. If the second parameter to it is <code>NULL</code>, then you get back a <code>raw</code> vector. It has a corresponding <code>unserialize()</code> which does what you think it does.</p>
<p>Modern browsers have <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">lots of ways to store things locally</a>. You can <em>persist</em> data as well, meaning you can store stuff — like R objects! — to be used later. This could come in handy!</p>
<p>Using those storage APIs directly is, quite frankly, painful. However, that <a href="https://localforage.github.io/localForage/"><code>localForage</code></a> library abstracts the pain away, providing familiar key/value idioms to store, retrieve, and remove persisted copies of any data. That means we can do something like this:</p>
<pre class="shiki " style="background-color: #0b0e14" tabindex="0"><code><span class="line"><span style="color: #FF8F40">const</span><span style="color: #BFBDB6"> res </span><span style="color: #F29668">=</span><span style="color: #BFBDB6"> </span><span style="color: #FF8F40">await</span><span style="color: #BFBDB6"> </span><span style="color: #FFB454">R</span><span style="color: #AAD94C">`serialize(mtcars, NULL)`</span></span>
<span class="line"><span style="color: #FF8F40">await</span><span style="color: #BFBDB6"> lf</span><span style="color: #F29668">.</span><span style="color: #FFB454">setItem</span><span style="color: #BFBDB6">(</span><span style="color: #AAD94C">'mtcars'</span><span style="color: #BFBDB6B3">,</span><span style="color: #BFBDB6"> res</span><span style="color: #F29668">.</span><span style="color: #BFBDB6">values)</span></span>
<span class="line"></span></code></pre>
<p>We can get that back from local storage via:</p>
<pre class="shiki " style="background-color: #0b0e14" tabindex="0"><code><span class="line"><span style="color: #FF8F40">const</span><span style="color: #BFBDB6"> opts </span><span style="color: #F29668">=</span><span style="color: #BFBDB6"> {</span></span>
<span class="line"><span style="color: #BFBDB6"> env</span><span style="color: #BFBDB6B3">:</span><span style="color: #BFBDB6"> { mtcars_serialized</span><span style="color: #BFBDB6B3">:</span><span style="color: #BFBDB6"> </span><span style="color: #FF8F40">await</span><span style="color: #BFBDB6"> lf</span><span style="color: #F29668">.</span><span style="color: #FFB454">getItem</span><span style="color: #BFBDB6">(</span><span style="color: #AAD94C">'mtcars'</span><span style="color: #BFBDB6">) }</span></span>
<span class="line"><span style="color: #BFBDB6">}</span></span>
<span class="line"><span style="color: #FF8F40">const</span><span style="color: #BFBDB6"> res </span><span style="color: #F29668">=</span><span style="color: #BFBDB6"> </span><span style="color: #FF8F40">await</span><span style="color: #BFBDB6"> webR</span><span style="color: #F29668">.</span><span style="color: #FFB454">evalR</span><span style="color: #BFBDB6">(</span><span style="color: #AAD94C">"unserialize(as.raw(mtcars_serialized))"</span><span style="color: #BFBDB6B3">,</span><span style="color: #BFBDB6"> opts)</span></span>
<span class="line"></span></code></pre>
<p>And, we can get rid of it just as easily:</p>
<pre class="shiki " style="background-color: #0b0e14" tabindex="0"><code><span class="line"><span style="color: #FF8F40">await</span><span style="color: #BFBDB6"> lf</span><span style="color: #F29668">.</span><span style="color: #FFB454">removeItem</span><span style="color: #BFBDB6">(</span><span style="color: #AAD94C">'mtcars'</span><span style="color: #BFBDB6">)</span></span>
<span class="line"></span></code></pre>
<p>If you open up Developer Tools, you'll see the bytes for the serialized first row of <code>mtcars</code> (I did not want to clutter up this page with hex string output). <code>res.values</code> contains the entire copy of <code>mtcars</code> in that raw format.</p>
<p>You should not go crazy with this feature, meaning try not to DoS your visitors with a gigantic amount of local storage. And, you will need to read up on <code>localForage</code> and web client storage in general to grok the nuances, limits, and "gotchas".</p>
<p>Reading up on R's <code>[un]serialize()</code> is also required, since there are limits to what it can do, especially with complex objects (an example of which might be parsed HTML/XML via {xml2}).</p>
<h2>Some JavaScript Stuff</h2>
<p><code>main.js</code> does all the hard work (it is also annotated), so I avoided putting large swaths of code blocks in this write-up.</p>
<p>There are four oddly (for me) reusable Lit web components included:</p>
<ul>
<li><code>status-message</code>: <em>(you know this one by now)</em></li>
<li><code>simple-message</code>: Basic message displayer without much adornment</li>
<li><code>action-button</code>: Generic action button <em>(pass in a label and click handler)</em></li>
<li><code>data-frame-view</code>: It doesn't validate that you passed in a valid data frame (i.e., a full <a href="https://docs.r-wasm.org/webr/latest/api/js/modules/RObject.html#webrdatajsnull"><code>WebRDataJsNull</code></a> <code>data.frame</code> structure), but if you do pass one in with a label and columns to display, it'll toss out a basic scrollable <code>div</code> with said table. It could be way more generic, but it should be sufficient for you to riff from.</li>
</ul>
<h2>FIN</h2>
<p>Go forth and persist all the things!</p>
<p>Source is on <a href="https://github.com/hrbrmstr/webr-localforage">GitHub</a></p>
<p style="text-align:center">Brought to you by @hrbrmstr</p><!-- extra body bits -->
</body>
</html>