-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spec for the NEW sandboxed Local Storage - Feedback appreciated #56
Comments
Very interesting! |
@j2l Yes. flexibility come with complexity. Prefer simple 1 unless no 2 has real benefit. |
Finally got to read this. This looks great and it would solve some of the problems I've been facing! |
@drwasho the reason I came up with option 2 was because you mentioned to me how the first approach wouldn't work for you. Maybe you can weigh in on the conversation on whether option 2 actually solves your problem, and if so, tell us why that works better than option 1 for you, so we can make a better decision moving forward? |
For what I'm building, there were 2 reasons why I decided not to use the option 1 approach:
And 2 is the big one. This is particularly relevant when you have views that are submitting a complex form, like an ecommerce listing. For example, imagine I have a parent view where I set most of the listing data, but I also have a child review where I set the shipping options. If I set shipping options in the child view, then when I go to create a new listing at another time, the shipping options from the previous listings will persist in state. The same would be true for listing options or any other detailed complex data that needs its own child view. Ultimately this means means I’ll end up creating listings with the details of old listings unless some very bad UI/UX is introduced. Alternatively I prefer to have a complex view + mix-ins (which make it manageable) where all the variables are local and easy to manipulate/clear. So I think option 2 is the best here, and I'd add that it would be even better if we had an However, my real preference is to have a global key:value storage that is read/write accessible from any view (actually configurable, as in X view can read and Y view can read/write). This does become a bit of a security headache when you have views that are remotely hosted, but for those of us creating production apps with locally stored views, it is less of a problem. |
@drwasho, it makes sense. |
Two comments:
|
@Joshfindit Yes that was the idea. In case of the second spec (which I am leaning towards), there's an attribute called
In this case the |
OK I made some updates. Here's the new syntax. This will probably be the final version that will roll out unless someone finds a bug or weird cases I haven't thought of. Before I explain, you can try out a working demo at https://jasonbase.com/things/amWz Basically the example creates a "chatroom" but everything is stored locally. Note that you need the $state branch code to get this to work. Anyway, here's the new syntax. Please let me know if you have any thoughts. Syntax v2.0Consists of :
All of the use a key format that looks like this: For example: This means you can't do things like Here are the details: $state.setSetting one attr.
Setting multiple attrs
$state.getGetting one attr. In this case, it retrieves the stored value at
Getting multiple attrs. Like above, it fetches the value and passes them down to the succeeding action as
$state.resetResetting a single attr. Notice the options takes an array.
Resetting multiple attrs
|
Syntax v3.0OK today I have another proposal. The v2.0 definitely works but I wasn't completely satisfied (especially the So this time I tried to come up with a syntax that will:
Anyway, here it goes: A. Setting stateIn this version, 1. A single attribute for a single source{
"type": "$state.set",
"options": {
"https://jasonbase.com/things/3nf.json": {
"username": "ethan"
}
}
} 2. Multiple attributes from a single source{
"type": "$state.set",
"options": {
"https://jasonbase.com/things/3nf.json": {
"username": "ethan",
"email": "ethan.gliechtenstein@gmail.com"
}
}
} 3. Multiple attributes from multiple sources{
"type": "$state.set",
"options": {
"https://jasonbase.com/things/3nf.json": {
"username": "ethan",
"email": "ethan.gliechtenstein@gmail.com"
},
"https://jasonbase.com/things/333.json": {
"lastUpdated": "{{Date.now().toString()}}"
}
}
} B. Getting stateOne thing I didn't like about previous approaches was you have to manually call {
"type": "label",
"text": "{{$state['https://jasonbase.com/things/cnf.json'].username}}"
} If the URL feels too bulky we can always move this to {
"$jason": {
"head": {
"actions": {
"$load": {
"type": "$set",
"options": {
"db": "{{$state['https://jasonbase.com/things/cnf.json']}}"
},
"success": {
"type": "$render"
}
}
},
"templates": {
"body": {
"sections": [{
"items": [{
"type": "label",
"text": "{{$get.db.username}}"
}]
}]
}
}
}
}
} C. Resetting State1. Resetting a single attribute from a single source{
"type": "$state.reset",
"options": {
"https://jasonbase.com/things/33n.json": ["email"]
}
} 2. Resetting multiple attributes from a single source{
"type": "$state.reset",
"options": {
"https://jasonbase.com/things/3nf.json": ["username", "email"]
}
} 3. Resetting multiple attributes from multiple sources{
"type": "$state.reset",
"options": {
"https://jasonbase.com/things/3nf.json": ["username", "email"],
"https://jasonbase.com/things/33n.json": ["lastUpdated"]
}
} If anyone sees something weird, please point out. Otherwise i'm gonna try to write an implementation for this in the next couple of days and test it out. Thanks |
I'm sure I'm missing a LOT .. but why not json data into and out of indexeddb something like this? |
@moses5407 the problem is more about "how" to express the get/set/reset actions in pure JSON markup, than how to implement it internally. The implementation itself is easy like you said, it will be pretty much about storing JSON under the URL namespace. What's been difficult was coming up with a concise and consistent way of expressing these actions in pure JSON. Or maybe I misunderstood what you said? Could you clarify if I got your idea wrong? Thanks! |
You didn't misunderstand. :-) In my simple mind, I was envisioning simply iterating over any changed data set and updating the indexeddb datastore or get-ing data from the indexeddb datastore in an offline condition. I'm slowwwwwly trying to get up to speed on a number of things and offline first is only one of them. var objstore = db.transaction([STORE], "readwrite").objectStore(STORE); |
Hey @gliechtenstein 1. Setting state
2. Getting state
👏 Masterpiece 3. Reseting state Makes sense. LGTM. |
@gliechtenstein could you just use "global" as your namespace for the cache and write it from wherever? I would assume this latest model allows that? |
@hoffmabc it's for security reasons. Jasonette is basically a browser for apps. This means it is possible to cross over between different apps (the soul) within the same app (the shell). I think this is one of the biggest strengths of Jasonette, but at the same time can be a security hole. I think it's easy to understand if you think in parallel with how web browsers work. They have localstorage but each localstorage is sandboxed to the parent URL. Here's a hypothetical scenario where it could go wrong if we didn't have the sandbox model:
App 1 may or may not have intended it, but the point is that there is no way to restrict this if we keep a single global storage. The idea was that, by tying these with URL it's much easier to handle permissions (For example App 1 can decide to allow access to its own "global" storage to only a selected set of URLs) This can be a powerful feature for an app, because you can allow access to the master localstorage from child views (only if you want), as well as specify strict permission policy between views, allowing for a more decentralized and customized implementation of apps. Hope this makes sense. If you have any suggestions or different ideas please share. |
OK I change my mind. I have decided to roll out the "global variable" everyone wants, instead of trying to keep trying to come up with the best solution for a sandboxed storage. It's much simpler than everything I've mentioned above because it's just a single global scope, and there is no URL. Example syntax: Setting:
Then you can use the
You can also remove the key from the global namespace:
The reason I changed my mind is:
The biggest benefit of this unrestricted global variable approach is that it's as simple as it can get, which is what everyone would want I presume. Anyway here's a simple TODO app using the global variable where the parent view opens a child view as a modal where it adds an item and returns back to the parent. I think this feature will make Anyway, check out the branch: https://github.com/Jasonette/JASONETTE-iOS/tree/global The code is annotated to show you how to use them (Jasonette/JASONETTE-iOS@525bbe9) and you can also refer to the example I shared above. I plan to merge this soon, because this is super simple and there's not much that can go wrong with this. But if anyone has feedback on anything such as the naming (currently it uses Thanks! |
NOW I understand the reason for the sandboxed storage. Thanks for that explanation about APP1 calling APP2 and the possible resulting data confusion. |
I also get the reasons behind your initial setup. Thanks |
One of the most frequently requested features is local key/value storage. This is a sensitive problem since it's directly related to security. Browsers have sandboxing for many good reasons. This is why I've been delaying making a decision on this until I've come up with a couple of ideas.
Now I have, and wanted to share. Note that none of the ideas involve having a single global storage, but involves having a local storage sandbox per each URL and accessing them from one view to another.
Here are the two:
1. Readonly exports
If you are aware of javascript module system you can think of this as a similar method. We would introduce two new actions
$state.export
and$state.import
. Users would need to first visit a URL to "export" all the values it wants to export, and then once that's done, other views can reference the value using$state.import
. If you try to$state.import
before it's been exported, it will not return the value;I actually took a shot at a quick prototype over at $state branch The code is commented so it should be self-explanatory how it should work.
An example code:
Exporting:
Importing
It works pretty similarly to how Javascript module system works. you can export and other modules can import to use them, but they are readonly (the similarity to Javascript module system is another reason why this feels simpler)
But like I said, not being able to write has some drawbacks, which is why I have another idea:
2. Read/Write state variables
Here, you not only can read from another URL's state, but also can write to it. Here the API would consist of:
$state.set
and$state.get
.Since being able to write to another view's sandbox is a big deal in terms of security, we also need another security measure. In this case we will also need to explicitly state the public state variables so that only those state variables can be set from outside of the view. This may look something like this:
Basically we are stating that anyone can set the state variable for this URL but ONLY the "username" and "email" variables.
I've attached below a full example of what it would look like. But before we go on, just a quick explanation on how this works:
abc.json
'sselected
state variable via$state.set
, and returns via$back
action.abc.json
, the$show
event triggers$state.get
and renders the view based on the state we've just set.abc.json
123.json
$state.get
, we need to query the the state's owner URL (one network/file request) to check its$jason.head.state
attribute to make sure the attribute has been declared accessible. (The first solution I suggested above doesn't have this problem since the variable will simply be empty if it hasn't been already exported)Feedback appreciated
I wanted to share here because I'm still trying to decide on this since people have been requesting this either directly or indirectly (a lot of the problems people talk about on the forum can be made easier by supporting this, for example almost all bugs/issues related to tab bar or $params will become irrelevant when we support this since we can deprecate $params and just use the
$state
instead, which is much more robust and flexible).I would appreciate any kind of feedback on either of the two. Especially if you have a better idea or improvement, or any security holes I missed, please feel free to share.
Thanks!
The text was updated successfully, but these errors were encountered: