{"id":1524,"date":"2011-03-07T08:44:52","date_gmt":"2011-03-07T16:44:52","guid":{"rendered":"http:\/\/blog.mozilla.org\/webdev\/?p=1524"},"modified":"2011-03-07T08:44:52","modified_gmt":"2011-03-07T16:44:52","slug":"datastore-an-abstraction-layer-for-storing-data-and-processing-requests","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/","title":{"rendered":"DataStore &#8211; an abstraction layer for storing data and processing requests"},"content":{"rendered":"<p><em>Note: this is a repost from my blog. You can find the original post <a href=\"http:\/\/embrangler.com\/2011\/03\/datastore-an-abstraction-layer\/\">here.<\/a> Please head over there for comments.<\/em><\/p>\n<h2 id='table_of_contents'>Table of contents<\/h2>\n<ol>\n<li><a href='#idea'>Idea<\/a><\/li>\n<li><a href='#from_past_to_present_what_now'>From past to present, what now?<\/a><\/li>\n<li><a href='#what_does_better_mean'>What does better mean?<\/a><\/li>\n<li><a href='#why_should_you_care'>Why should you care?<\/a><\/li>\n<li><a href='#my_scenario'>My scenario<\/a><\/li>\n<li><a href='#what_worked_for_me'>What worked for me<\/a><\/li>\n<li><a href='#proposed_features'>Proposed features<\/a><\/li>\n<li><a href='#help'>Help!<\/a><\/li>\n<\/ol>\n<h2 id='idea'>Idea<\/h2>\n<p>A library to unify all the different data storage\/retrieval\/sending\/receiving API&#8217;s such as XMLHttpRequest, WebSockets, localStorage, IndexedDB, and make it easier to use any number of them at once.<\/p>\n<h2 id='from_past_to_present_what_now'>From past to present, what now?<\/h2>\n<p><strong>Past:<\/strong> Before, all we had was AJAX requests. Really.<\/p>\n<p><strong>To present:<\/strong> With the new technologies coming up in the HTML5 era, we&#8217;ve got localStorage and IndexedDB, WebSockets, node.js, and more. Hectic.<\/p>\n<p><strong>What now?<\/strong> Don&#8217;t you wish there was a <em>better way<\/em> to send and receive data in the browser?<\/p>\n<h2 id='what_does_better_mean'>What does better mean?<\/h2>\n<p>My general goals for this are:<\/p>\n<ol>\n<li>Simple key\/value store common abstraction.<\/li>\n<li>Pluggable handlers for each type of send\/receive.<\/li>\n<li>Use other abstractions specified in each handler (library surfaces your API as well).<\/li>\n<li>Straightforward way to define flow of data. <a href='#flow_of_data'>More on this later.<\/a><\/li>\n<\/ol>\n<p>Anything else you wish it could do?<\/p>\n<h2 id='why_should_you_care'>Why should you care?<\/h2>\n<p>Short answer: <strong>maintenance, scalability, flexibility.<\/strong><\/p>\n<p>As these technologies become widely supported, you will start seeing a common problem for websites heavily relying on AJAX (or any kind of data transfer without page reloads): <em>how do you take advantage of them without rewriting your entire codebase every time there&#8217;s a new technology (API\/storage engine\/etc) coming out?<\/em><\/p>\n<h2 id='my_scenario'>My scenario<\/h2>\n<p>The whole reason I got thinking about this was because it happened to me. And it was frustrating.<\/p>\n<p>I had this client-side application using <a href='http:\/\/api.jquery.com\/jQuery.ajax\/'>jQuery.ajax requests<\/a>, and I wanted to take advantage of localStorage for some of them, for data that I didn&#8217;t need to get from the server on every page load.<\/p>\n<p>I considered:<\/p>\n<ul>\n<li>Quick&#8217;n&#8217;dirty: Rewrite these pieces of the application to do both localStorage and ajax requests as fallback.<\/li>\n<li><strong><a href='#what_worked_for_me'>Slightly better:<\/a><\/strong> A library that&#8217;s flexible enough for my purposes.<\/li>\n<li><a href='#proposed_features'>Ideal:<\/a> A library that would allow me to enable\/disable localStorage as an intermediary step on a per-request basis, make it easy to add IndexedDB support later, etc.<\/li>\n<\/ul>\n<h2 id='what_worked_for_me'>What worked for me<\/h2>\n<p>The simpler thing I went with was a Data object with a couple of functions.<\/p>\n<p>Example usage:<\/p>\n<div class='highlight'>\n<pre><code class='javascript'><span class='lineno'> 1<\/span> <span class='c1'>\/\/ main.js<\/span>\r\n<span class='lineno'> 2<\/span> <span class='nb'>window<\/span><span class='p'>.<\/span><span class='nx'>data<\/span> <span class='o'>=<\/span> <span class='k'>new<\/span> <span class='nx'>DataStore<\/span><span class='p'>({<\/span>\r\n<span class='lineno'> 3<\/span>     <span class='nx'>url<\/span><span class='o'>:<\/span> <span class='s1'>&#39;\/fetch_new_data&#39;<\/span><span class='p'>,<\/span>\r\n<span class='lineno'> 4<\/span>     <span class='c1'>\/\/ show a spinny tangy<\/span>\r\n<span class='lineno'> 5<\/span>     <span class='nx'>sync_before<\/span><span class='o'>:<\/span> <span class='kd'>function<\/span> <span class='nx'>showSyncInProgress<\/span><span class='p'>()<\/span> <span class='p'>{<\/span> <span class='p'>...<\/span> <span class='p'>},<\/span>\r\n<span class='lineno'> 6<\/span>     <span class='c1'>\/\/ hide the spinny thingy, maybe show a fading notification<\/span>\r\n<span class='lineno'> 7<\/span>     <span class='nx'>sync_success<\/span><span class='o'>:<\/span> <span class='kd'>function<\/span> <span class='nx'>showSyncDone<\/span><span class='p'>()<\/span> <span class='p'>{<\/span> <span class='p'>...<\/span> <span class='p'>},<\/span>\r\n<span class='lineno'> 8<\/span>     <span class='c1'>\/\/ hide the spinny thingy, definitely show some message<\/span>\r\n<span class='lineno'> 9<\/span>     <span class='nx'>sync_error<\/span><span class='o'>:<\/span> <span class='kd'>function<\/span> <span class='nx'>showSyncFailed<\/span><span class='p'>()<\/span> <span class='p'>{<\/span> <span class='p'>...<\/span> <span class='p'>}<\/span>\r\n<span class='lineno'>10<\/span> <span class='p'>}<\/span>\r\n<span class='lineno'>11<\/span> \r\n<span class='lineno'>12<\/span> <span class='c1'>\/\/ example request<\/span>\r\n<span class='lineno'>13<\/span> <span class='kd'>var<\/span> <span class='nx'>i<\/span> <span class='o'>=<\/span> <span class='mi'>0<\/span><span class='p'>;<\/span>\r\n<span class='lineno'>14<\/span> <span class='nb'>window<\/span><span class='p'>.<\/span><span class='nx'>data<\/span><span class='p'>.<\/span><span class='nx'>process_request<\/span><span class='p'>({<\/span>\r\n<span class='lineno'>15<\/span>     <span class='nx'>ajax<\/span><span class='o'>:<\/span> <span class='p'>{<\/span><span class='nx'>url<\/span><span class='o'>:<\/span> <span class='s1'>&#39;\/new_comment&#39;<\/span><span class='p'>,<\/span> <span class='nx'>type<\/span><span class='o'>:<\/span> <span class='s1'>&#39;POST&#39;<\/span><span class='p'>,<\/span>\r\n<span class='lineno'>16<\/span>            <span class='nx'>data<\/span><span class='o'>:<\/span> <span class='nx'>$<\/span><span class='p'>(<\/span><span class='s1'>&#39;#comment-form&#39;<\/span><span class='p'>).<\/span><span class='nx'>serialize<\/span><span class='p'>()},<\/span>\r\n<span class='lineno'>17<\/span>     <span class='nx'>key<\/span><span class='o'>:<\/span> <span class='s1'>&#39;comment_&#39;<\/span> <span class='o'>+<\/span> <span class='p'>(<\/span><span class='nx'>i<\/span><span class='o'>++<\/span><span class='p'>),<\/span>\r\n<span class='lineno'>18<\/span>     <span class='nx'>value<\/span><span class='o'>:<\/span> <span class='p'>{<\/span><span class='s1'>&#39;author&#39;<\/span><span class='o'>:<\/span> <span class='nx'>$<\/span><span class='p'>(<\/span><span class='s1'>&#39;#comment-form .author&#39;<\/span><span class='p'>).<\/span><span class='nx'>val<\/span><span class='p'>(),<\/span>\r\n<span class='lineno'>19<\/span>             <span class='s1'>&#39;text&#39;<\/span><span class='o'>:<\/span> <span class='nx'>$<\/span><span class='p'>(<\/span><span class='s1'>&#39;#comment-form .text&#39;<\/span><span class='p'>).<\/span><span class='nx'>val<\/span><span class='p'>()}<\/span>\r\n<span class='lineno'>20<\/span> <span class='p'>});<\/span>\r\n<\/code><\/pre>\n<\/div>\n<p><code>ajax.data<\/code> and <code>value<\/code> are actually very similar, with an important exception in most applications (e.g. Django): the csrftoken. We don&#8217;t need to store that in localStorage for every request. So I chose to keep the two completely separate. You could subclass DataStore and make it save you this extra work per request.<\/p>\n<p>Below is an example implementation (<a href='http:\/\/embrangler.com\/files\/code\/datastore\/datastore.js'>raw file<\/a>):<\/p>\n<div class='highlight'>\n<pre><code class='javascript'><span class='lineno'>  1<\/span> <span class='cm'>\/* This depends on Crockford&#39;s json2.js<\/span>\r\n<span class='lineno'>  2<\/span> <span class='cm'> * from https:\/\/github.com\/douglascrockford\/JSON-js<\/span>\r\n<span class='lineno'>  3<\/span> <span class='cm'> * Options:<\/span>\r\n<span class='lineno'>  4<\/span> <span class='cm'> *     - url: function()<\/span>\r\n<span class='lineno'>  5<\/span> <span class='cm'> *     - sync_before: function()<\/span>\r\n<span class='lineno'>  6<\/span> <span class='cm'> *     - sync_success: function()<\/span>\r\n<span class='lineno'>  7<\/span> <span class='cm'> *     - sync_error: function()<\/span>\r\n<span class='lineno'>  8<\/span> <span class='cm'> *\/<\/span>\r\n<span class='lineno'>  9<\/span> <span class='kd'>function<\/span> <span class='nx'>DataStore<\/span><span class='p'>(<\/span><span class='nx'>options<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 10<\/span>     <span class='nb'>window<\/span><span class='p'>.<\/span><span class='nx'>data<\/span> <span class='o'>=<\/span> <span class='k'>this<\/span><span class='p'>;<\/span>\r\n<span class='lineno'> 11<\/span>     <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>storage<\/span> <span class='o'>=<\/span> <span class='nb'>window<\/span><span class='p'>.<\/span><span class='nx'>localStorage<\/span><span class='p'>;<\/span>\r\n<span class='lineno'> 12<\/span>     <span class='c1'>\/\/ date of last time we synced<\/span>\r\n<span class='lineno'> 13<\/span>     <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span> <span class='o'>=<\/span> <span class='kc'>null<\/span><span class='p'>;<\/span>\r\n<span class='lineno'> 14<\/span>     <span class='c1'>\/\/ queue of requests, populated if offline<\/span>\r\n<span class='lineno'> 15<\/span>     <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>queue<\/span> <span class='o'>=<\/span> <span class='p'>[];<\/span>\r\n<span class='lineno'> 16<\/span> \r\n<span class='lineno'> 17<\/span>     <span class='cm'>\/**<\/span>\r\n<span class='lineno'> 18<\/span> <span class='cm'>     * Gets data stored at `key`; `key` is a string<\/span>\r\n<span class='lineno'> 19<\/span> <span class='cm'>     *\/<\/span>\r\n<span class='lineno'> 20<\/span>     <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>get_data<\/span> <span class='o'>=<\/span> <span class='kd'>function<\/span> <span class='p'>(<\/span><span class='nx'>key<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 21<\/span>         <span class='kd'>var<\/span> <span class='nx'>str_data<\/span> <span class='o'>=<\/span> <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>storage<\/span><span class='p'>.<\/span><span class='nx'>getItem<\/span><span class='p'>(<\/span><span class='nx'>key<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 22<\/span>         <span class='k'>return<\/span> <span class='nx'>JSON<\/span><span class='p'>.<\/span><span class='nx'>parse<\/span><span class='p'>(<\/span><span class='nx'>str_data<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 23<\/span>     <span class='p'>}<\/span>\r\n<span class='lineno'> 24<\/span> \r\n<span class='lineno'> 25<\/span>     <span class='cm'>\/**<\/span>\r\n<span class='lineno'> 26<\/span> <span class='cm'>     * Sets data at `key`; `key` is a string<\/span>\r\n<span class='lineno'> 27<\/span> <span class='cm'>     *\/<\/span>\r\n<span class='lineno'> 28<\/span>     <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>set_data<\/span> <span class='o'>=<\/span> <span class='kd'>function<\/span> <span class='p'>(<\/span><span class='nx'>key<\/span><span class='p'>,<\/span> <span class='nx'>data<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 29<\/span>         <span class='kd'>var<\/span> <span class='nx'>str_data<\/span> <span class='o'>=<\/span> <span class='nx'>JSON<\/span><span class='p'>.<\/span><span class='nx'>stringify<\/span><span class='p'>(<\/span><span class='nx'>data<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 30<\/span>         <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>storage<\/span><span class='p'>.<\/span><span class='nx'>setItem<\/span><span class='p'>(<\/span><span class='nx'>key<\/span><span class='p'>,<\/span> <span class='nx'>str_data<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 31<\/span>     <span class='p'>}<\/span>\r\n<span class='lineno'> 32<\/span> \r\n<span class='lineno'> 33<\/span>     <span class='cm'>\/**<\/span>\r\n<span class='lineno'> 34<\/span> <span class='cm'>     * Syncs data between local storage and server, depending on<\/span>\r\n<span class='lineno'> 35<\/span> <span class='cm'>     * modifications and online status.<\/span>\r\n<span class='lineno'> 36<\/span> <span class='cm'>     *\/<\/span>\r\n<span class='lineno'> 37<\/span>     <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>sync_data<\/span> <span class='o'>=<\/span> <span class='kd'>function<\/span> <span class='p'>()<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 38<\/span>         <span class='c1'>\/\/ must be online to sync<\/span>\r\n<span class='lineno'> 39<\/span>         <span class='k'>if<\/span> <span class='p'>(<\/span><span class='o'>!<\/span><span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>is_online<\/span><span class='p'>())<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 40<\/span>             <span class='k'>return<\/span> <span class='kc'>false<\/span><span class='p'>;<\/span>\r\n<span class='lineno'> 41<\/span>         <span class='p'>}<\/span>\r\n<span class='lineno'> 42<\/span> \r\n<span class='lineno'> 43<\/span>         <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span> <span class='o'>=<\/span> <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>get_data<\/span><span class='p'>(<\/span><span class='s1'>&#39;last_sync&#39;<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 44<\/span> \r\n<span class='lineno'> 45<\/span>         <span class='c1'>\/\/ have we never synced before in this browser?<\/span>\r\n<span class='lineno'> 46<\/span>         <span class='k'>if<\/span> <span class='p'>(<\/span><span class='o'>!<\/span><span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 47<\/span>             <span class='c1'>\/\/ first-time setup<\/span>\r\n<span class='lineno'> 48<\/span>             <span class='c1'>\/\/ ...<\/span>\r\n<span class='lineno'> 49<\/span>             <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span> <span class='o'>=<\/span> <span class='p'>{};<\/span>\r\n<span class='lineno'> 50<\/span>             <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span><span class='p'>.<\/span><span class='nx'>when<\/span> <span class='o'>=<\/span> <span class='k'>new<\/span> <span class='nb'>Date<\/span><span class='p'>().<\/span><span class='nx'>getTime<\/span><span class='p'>();<\/span>\r\n<span class='lineno'> 51<\/span>             <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span><span class='p'>.<\/span><span class='nx'>is_modified<\/span> <span class='o'>=<\/span> <span class='kc'>false<\/span><span class='p'>;<\/span>\r\n<span class='lineno'> 52<\/span>         <span class='p'>}<\/span>\r\n<span class='lineno'> 53<\/span> \r\n<span class='lineno'> 54<\/span>         <span class='k'>if<\/span> <span class='p'>(<\/span><span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span><span class='p'>.<\/span><span class='nx'>is_modified<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 55<\/span>             <span class='kd'>var<\/span> <span class='nx'>request_options<\/span><span class='p'>;<\/span>\r\n<span class='lineno'> 56<\/span>             <span class='c1'>\/\/ sync modified data<\/span>\r\n<span class='lineno'> 57<\/span>             <span class='c1'>\/\/ you can pass callbacks here too<\/span>\r\n<span class='lineno'> 58<\/span>             <span class='k'>while<\/span> <span class='p'>(<\/span><span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>queue<\/span><span class='p'>.<\/span><span class='nx'>length<\/span> <span class='o'>&gt;<\/span> <span class='mi'>0<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 59<\/span>                 <span class='nx'>request_options<\/span> <span class='o'>=<\/span> <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>queue<\/span><span class='p'>.<\/span><span class='nx'>pop<\/span><span class='p'>();<\/span>\r\n<span class='lineno'> 60<\/span>                 <span class='nx'>$<\/span><span class='p'>.<\/span><span class='nx'>ajax<\/span><span class='p'>(<\/span><span class='nx'>request_options<\/span><span class='p'>.<\/span><span class='nx'>ajax<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 61<\/span>             <span class='p'>}<\/span>\r\n<span class='lineno'> 62<\/span>             <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>set_data<\/span><span class='p'>(<\/span><span class='s1'>&#39;queue&#39;<\/span><span class='p'>,<\/span> <span class='p'>[]);<\/span>\r\n<span class='lineno'> 63<\/span>             <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span><span class='p'>.<\/span><span class='nx'>is_modified<\/span> <span class='o'>=<\/span> <span class='kc'>false<\/span><span class='p'>;<\/span>\r\n<span class='lineno'> 64<\/span>         <span class='p'>}<\/span>\r\n<span class='lineno'> 65<\/span>         <span class='c1'>\/\/ data is synced, update sync time<\/span>\r\n<span class='lineno'> 66<\/span>         <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>set_data<\/span><span class='p'>(<\/span><span class='s1'>&#39;last_sync&#39;<\/span><span class='p'>,<\/span> <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 67<\/span> \r\n<span class='lineno'> 68<\/span>         <span class='c1'>\/\/ get modified data from the server here<\/span>\r\n<span class='lineno'> 69<\/span>        <span class='nx'>$<\/span><span class='p'>.<\/span><span class='nx'>ajax<\/span><span class='p'>({<\/span>\r\n<span class='lineno'> 70<\/span>             <span class='nx'>type<\/span><span class='o'>:<\/span> <span class='s1'>&#39;POST&#39;<\/span><span class='p'>,<\/span>\r\n<span class='lineno'> 71<\/span>             <span class='nx'>url<\/span><span class='o'>:<\/span> <span class='nx'>options<\/span><span class='p'>.<\/span><span class='nx'>url<\/span><span class='p'>,<\/span>\r\n<span class='lineno'> 72<\/span>             <span class='nx'>dataType<\/span><span class='o'>:<\/span> <span class='s1'>&#39;json&#39;<\/span><span class='p'>,<\/span>\r\n<span class='lineno'> 73<\/span>             <span class='nx'>data<\/span><span class='o'>:<\/span> <span class='p'>{<\/span><span class='s1'>&#39;last_sync&#39;<\/span><span class='o'>:<\/span> <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span><span class='p'>.<\/span><span class='nx'>sync_date<\/span><span class='p'>},<\/span>\r\n<span class='lineno'> 74<\/span>             <span class='nx'>beforeSend<\/span><span class='o'>:<\/span>\r\n<span class='lineno'> 75<\/span>                 <span class='c1'>\/\/ here you can show some &quot;sync in progress&quot; icon<\/span>\r\n<span class='lineno'> 76<\/span>                 <span class='nx'>options<\/span><span class='p'>.<\/span><span class='nx'>sync_before<\/span><span class='p'>,<\/span>\r\n<span class='lineno'> 77<\/span>             <span class='nx'>error<\/span><span class='o'>:<\/span>\r\n<span class='lineno'> 78<\/span>                 <span class='c1'>\/\/ an error callback should be passed in to this Data<\/span>\r\n<span class='lineno'> 79<\/span>                 <span class='c1'>\/\/ object and would be called here<\/span>\r\n<span class='lineno'> 80<\/span>                 <span class='nx'>options<\/span><span class='p'>.<\/span><span class='nx'>sync_error<\/span><span class='p'>,<\/span>\r\n<span class='lineno'> 81<\/span>             <span class='nx'>success<\/span><span class='o'>:<\/span> <span class='kd'>function<\/span> <span class='p'>(<\/span><span class='nx'>response<\/span><span class='p'>,<\/span> <span class='nx'>textStatus<\/span><span class='p'>,<\/span> <span class='nx'>request<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 82<\/span>                 <span class='c1'>\/\/ callback for success<\/span>\r\n<span class='lineno'> 83<\/span>                 <span class='nx'>options<\/span><span class='p'>.<\/span><span class='nx'>sync_success<\/span><span class='p'>(<\/span>\r\n<span class='lineno'> 84<\/span>                     <span class='nx'>response<\/span><span class='p'>,<\/span> <span class='nx'>textStatus<\/span><span class='p'>,<\/span> <span class='nx'>request<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 85<\/span>             <span class='p'>}<\/span>\r\n<span class='lineno'> 86<\/span>         <span class='p'>});<\/span>\r\n<span class='lineno'> 87<\/span> \r\n<span class='lineno'> 88<\/span> \r\n<span class='lineno'> 89<\/span>     <span class='cm'>\/**<\/span>\r\n<span class='lineno'> 90<\/span> <span class='cm'>     * Process a request. This is where all the magic happens.<\/span>\r\n<span class='lineno'> 91<\/span> <span class='cm'>     *\/<\/span>\r\n<span class='lineno'> 92<\/span>     <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>process_request<\/span> <span class='o'>=<\/span> <span class='kd'>function<\/span><span class='p'>(<\/span><span class='nx'>request_options<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 93<\/span>         <span class='nx'>request_options<\/span><span class='p'>.<\/span><span class='nx'>beforeSend<\/span><span class='p'>();<\/span>\r\n<span class='lineno'> 94<\/span>         <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>set_data<\/span><span class='p'>(<\/span><span class='nx'>request_options<\/span><span class='p'>.<\/span><span class='nx'>key<\/span><span class='p'>,<\/span> <span class='nx'>request_options<\/span><span class='p'>.<\/span><span class='nx'>value<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 95<\/span> \r\n<span class='lineno'> 96<\/span>         <span class='k'>if<\/span> <span class='p'>(<\/span><span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>is_online<\/span><span class='p'>())<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 97<\/span>             <span class='nx'>$<\/span><span class='p'>.<\/span><span class='nx'>ajax<\/span><span class='p'>(<\/span><span class='nx'>request_options<\/span><span class='p'>.<\/span><span class='nx'>ajax<\/span><span class='p'>);<\/span>\r\n<span class='lineno'> 98<\/span>         <span class='p'>}<\/span> <span class='k'>else<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'> 99<\/span>             <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>queue<\/span><span class='p'>.<\/span><span class='nx'>push<\/span><span class='p'>(<\/span><span class='nx'>request_options<\/span><span class='p'>);<\/span>\r\n<span class='lineno'>100<\/span>             <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span><span class='p'>.<\/span><span class='nx'>is_modified<\/span> <span class='o'>=<\/span> <span class='kc'>true<\/span><span class='p'>;<\/span>\r\n<span class='lineno'>101<\/span>             <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>set_data<\/span><span class='p'>(<\/span><span class='s1'>&#39;last_sync&#39;<\/span><span class='p'>,<\/span> <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>last_sync<\/span><span class='p'>);<\/span>\r\n<span class='lineno'>102<\/span>             <span class='c1'>\/\/ there are issues with this, storing functions as<\/span>\r\n<span class='lineno'>103<\/span>             <span class='c1'>\/\/ strings is not a good idea :)<\/span>\r\n<span class='lineno'>104<\/span>             <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>set_data<\/span><span class='p'>(<\/span><span class='s1'>&#39;queue&#39;<\/span><span class='p'>,<\/span> <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>queue<\/span><span class='p'>);<\/span>\r\n<span class='lineno'>105<\/span>         <span class='p'>}<\/span>\r\n<span class='lineno'>106<\/span> \r\n<span class='lineno'>107<\/span>         <span class='nx'>request_options<\/span><span class='p'>.<\/span><span class='nx'>processed<\/span><span class='p'>();<\/span>\r\n<span class='lineno'>108<\/span>     <span class='p'>}<\/span>\r\n<span class='lineno'>109<\/span> \r\n<span class='lineno'>110<\/span>     <span class='cm'>\/**<\/span>\r\n<span class='lineno'>111<\/span> <span class='cm'>     * Return true if online, false otherwise.<\/span>\r\n<span class='lineno'>112<\/span> <span class='cm'>     *\/<\/span>\r\n<span class='lineno'>113<\/span>     <span class='k'>this<\/span><span class='p'>.<\/span><span class='nx'>is_online<\/span> <span class='o'>=<\/span> <span class='kd'>function<\/span> <span class='p'>()<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'>114<\/span>         <span class='k'>if<\/span> <span class='p'>(<\/span><span class='nx'>navigator<\/span> <span class='o'>&amp;&amp;<\/span> <span class='nx'>navigator<\/span><span class='p'>.<\/span><span class='nx'>onLine<\/span> <span class='o'>!==<\/span> <span class='kc'>undefined<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'>115<\/span>             <span class='k'>return<\/span> <span class='nx'>navigator<\/span><span class='p'>.<\/span><span class='nx'>onLine<\/span><span class='p'>;<\/span>\r\n<span class='lineno'>116<\/span>         <span class='p'>}<\/span>\r\n<span class='lineno'>117<\/span>         <span class='k'>try<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'>118<\/span>             <span class='kd'>var<\/span> <span class='nx'>request<\/span> <span class='o'>=<\/span> <span class='k'>new<\/span> <span class='nx'>XMLHttpRequest<\/span><span class='p'>();<\/span>\r\n<span class='lineno'>119<\/span>             <span class='nx'>request<\/span><span class='p'>.<\/span><span class='nx'>open<\/span><span class='p'>(<\/span><span class='s1'>&#39;GET&#39;<\/span><span class='p'>,<\/span> <span class='s1'>&#39;\/&#39;<\/span><span class='p'>,<\/span> <span class='kc'>false<\/span><span class='p'>);<\/span>\r\n<span class='lineno'>120<\/span>             <span class='nx'>request<\/span><span class='p'>.<\/span><span class='nx'>send<\/span><span class='p'>(<\/span><span class='kc'>null<\/span><span class='p'>);<\/span>\r\n<span class='lineno'>121<\/span>             <span class='k'>return<\/span> <span class='p'>(<\/span><span class='nx'>request<\/span><span class='p'>.<\/span><span class='nx'>status<\/span> <span class='o'>===<\/span> <span class='mi'>200<\/span><span class='p'>);<\/span>\r\n<span class='lineno'>122<\/span>         <span class='p'>}<\/span>\r\n<span class='lineno'>123<\/span>         <span class='k'>catch<\/span><span class='p'>(<\/span><span class='nx'>e<\/span><span class='p'>)<\/span> <span class='p'>{<\/span>\r\n<span class='lineno'>124<\/span>             <span class='k'>return<\/span> <span class='kc'>false<\/span><span class='p'>;<\/span>\r\n<span class='lineno'>125<\/span>         <span class='p'>}<\/span>\r\n<span class='lineno'>126<\/span>     <span class='p'>}<\/span>\r\n<span class='lineno'>127<\/span> <span class='p'>}<\/span>\r\n<\/code><\/pre>\n<\/div>\n<h2 id='proposed_features'>Proposed Features<\/h2>\n<p>The example API isn&#8217;t bad, but I think it could be better. Perhaps something along the lines of <a href='http:\/\/westcoastlogic.com\/lawnchair\/'>Lawnchair<\/a>. As I&#8217;m writing this, I realize that writing an API is going to take longer than I&#8217;d like &#8211; therefore, this will serve as a teaser and food for thought. Feedback is welcome.<\/p>\n<ul>\n<li>Add an <em>.each<\/em> method for iterating over retrieved objects (inspired by Lawnchair)<\/li>\n<li>Standard <code>DataStore.save, .get, .remove, etc.<\/code><\/li>\n<li>Support for these &#8220;storage engines&#8221;: localStorage, IndexedDB, send-to-server.<\/li>\n<li>Support for these request types: XMLHttpRequest, <a href='http:\/\/en.wikipedia.org\/wiki\/WebSockets'>WebSockets<\/a>.<\/li>\n<li>Store, at the very least, primitive values and JSON.<\/li>\n<li>Include callbacks for various stages in the process of a request, similar to jQuery.ajax, e.g. <code>beforeSend, complete, success, error<\/code>. Figure out a good way to do this at each layer (minimize confusion).<\/li>\n<li>For each request, specify which layers and in what order to go through. For example, if you want to store something in localStorage, IndexedDB, and send it to the server, you could do it in that order or the reverse.<\/li>\n<li>Control whether to go to the next layer type depending on whether the previous succeeded or failed. Say, if you want to send the request to server but that fails, try localStorage as a fallback. Or the opposite.<\/li>\n<li>Include a <code>.get_then_store<\/code> shortcut for getting the data from layer A and storing it in layer B?<\/li>\n<li>Extensible: as easy as <code>DataStore.addLayer(layerName, layerHandler)<\/code>, where layerHandler (obviously) implements some common API along with exposing some of its own, if necessary (e.g. ability to query or find, for IndexedDB).<\/li>\n<\/ul>\n<h2 id='help'>Help!<\/h2>\n<p>Hopefully my rant has gotten you thinking about the right approach. What would you like to see? What would make this something you would use and be happy with?<\/p>\n<p>If you are interested in getting involved with coding this, contact me at paulc at mozilla.com.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A library to unify all the different data storage\/retrieval\/sending\/receiving API&#8217;s such as XMLHttpRequest, WebSockets, localStorage, IndexedDB, and make it easier to use any number of them at once. <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/\">Continue reading<\/a><\/p>\n","protected":false},"author":211,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4713,288],"tags":[],"coauthors":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>DataStore - an abstraction layer for storing data and processing requests - Mozilla Web Development<\/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:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Paul Craciunoiu\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"6 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/\",\"url\":\"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/\",\"name\":\"DataStore - an abstraction layer for storing data and processing requests - Mozilla Web Development\",\"isPartOf\":{\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#website\"},\"datePublished\":\"2011-03-07T16:44:52+00:00\",\"dateModified\":\"2011-03-07T16:44:52+00:00\",\"author\":{\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/e5959c8c5a38805891ed2bd45e2f8d0c\"},\"breadcrumb\":{\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blog.mozilla.org\/webdev\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"DataStore &#8211; an abstraction layer for storing data and processing requests\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#website\",\"url\":\"https:\/\/blog.mozilla.org\/webdev\/\",\"name\":\"Mozilla Web Development\",\"description\":\"For make benefit of glorious tubes\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blog.mozilla.org\/webdev\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/e5959c8c5a38805891ed2bd45e2f8d0c\",\"name\":\"Paul Craciunoiu\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/image\/bcf0b623715b0faf8c35c90d58d6acd6\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/605bb18eedd69211a2a5783afe9a5ad6?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/605bb18eedd69211a2a5783afe9a5ad6?s=96&d=mm&r=g\",\"caption\":\"Paul Craciunoiu\"},\"description\":\"I'm a full time web developer for Mozilla, with main focus on Django, Python, and JavaScript. I have a B.S.C.S. from UCSC. I\u2019ve been involved with Mozilla since 2008. If you're interested in more about me and what I do, check out my blog or follow me on twitter.\",\"sameAs\":[\"http:\/\/embrangler.com\"],\"url\":\"https:\/\/blog.mozilla.org\/webdev\/author\/paulc-2\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"DataStore - an abstraction layer for storing data and processing requests - Mozilla Web Development","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:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/","twitter_misc":{"Written by":"Paul Craciunoiu","Est. reading time":"6 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/","url":"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/","name":"DataStore - an abstraction layer for storing data and processing requests - Mozilla Web Development","isPartOf":{"@id":"https:\/\/blog.mozilla.org\/webdev\/#website"},"datePublished":"2011-03-07T16:44:52+00:00","dateModified":"2011-03-07T16:44:52+00:00","author":{"@id":"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/e5959c8c5a38805891ed2bd45e2f8d0c"},"breadcrumb":{"@id":"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.mozilla.org\/webdev\/2011\/03\/07\/datastore-an-abstraction-layer-for-storing-data-and-processing-requests\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.mozilla.org\/webdev\/"},{"@type":"ListItem","position":2,"name":"DataStore &#8211; an abstraction layer for storing data and processing requests"}]},{"@type":"WebSite","@id":"https:\/\/blog.mozilla.org\/webdev\/#website","url":"https:\/\/blog.mozilla.org\/webdev\/","name":"Mozilla Web Development","description":"For make benefit of glorious tubes","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.mozilla.org\/webdev\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/e5959c8c5a38805891ed2bd45e2f8d0c","name":"Paul Craciunoiu","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/image\/bcf0b623715b0faf8c35c90d58d6acd6","url":"https:\/\/secure.gravatar.com\/avatar\/605bb18eedd69211a2a5783afe9a5ad6?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/605bb18eedd69211a2a5783afe9a5ad6?s=96&d=mm&r=g","caption":"Paul Craciunoiu"},"description":"I'm a full time web developer for Mozilla, with main focus on Django, Python, and JavaScript. I have a B.S.C.S. from UCSC. I\u2019ve been involved with Mozilla since 2008. If you're interested in more about me and what I do, check out my blog or follow me on twitter.","sameAs":["http:\/\/embrangler.com"],"url":"https:\/\/blog.mozilla.org\/webdev\/author\/paulc-2\/"}]}},"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/posts\/1524"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/users\/211"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/comments?post=1524"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/posts\/1524\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/media?parent=1524"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/categories?post=1524"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/tags?post=1524"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/coauthors?post=1524"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}