How to initialize localForage for mobile app - localforage

I'm new to mobile development and plan to build an app using plain HTML & jQuery, with Onsen UI.
I read that we can use localForage as a database and have a few questions.
Is it mandatory to have a database name for my app. If no, then other apps in mobile may also be using localForage. Will the DB be same then for all apps.
The document here says, config should be called before each action.
So, is it ok if it is initialized on page load like this:
$(document).ready(function(){
localforage.config({
name : 'myApp',
version : 1.0,
storeName : 'keyvaluepairs'
});
});
or should it be declared before each action (get, set, clear etc.)
How can we know that the action is triggering the desired database, as it is not specified in the action methods.
Is it mandatory to have a store name.

Is it mandatory to have a database name for my app. If no, then other apps in mobile may also be using localForage. Will the DB be same then for all apps.
A: Yes and no. It is in fact mandatory to have a database name. However if you leave it unset, the default value "localforage" is used.
The document here says, config should be called before each action. So, is it ok if it is initialized on page load like this ...
A: Yes, it totally fine to init in $(document).ready(cb). In fact it's fine to "initialize" at any time, as long as you make sure it happens before the first ever call to any real action (setItem/getItem, etc.).
How can we know that the action is triggering the desired database, as it is not specified in the action methods.
A: localforage can have multiple instances, whereas each instance is bound to only one database, (more precisely, it's bound to a specific store of that specific database). You know the action is targeting a specific database, because these actions are methods of a specific instance. No ambiguity here.
I personally suggest you name your instance explicitly:
var myAppDb = localforage.createInstance({
// these are the same options accepted by localforage.config()
name: 'myApp',
version : 1.0,
storeName : 'keyvaluepairs'
});
myAppDb.setItem('foo', 'bar');
This way you're 100% sure the action is fired on "myApp" database ;-)
Is it mandatory to have a store name.
Again, yes and no. But wait, hear me out, this one is a bit tricky.
Whereas the default database is actually named "localforage", store in localforage has this strange internal concept of un-named default store. I personally find it very confusing. And it behaves quite quirky when you use LOCALSTORAGE as driver.
So the rule of thumb is always name your store. Just treat it like mandatory. If you have only one store in one database, maybe name it "default". Sound better than "keyvaluepairs" don't you think?

let instance = localforage.createInstance({
driver : localforage.INDEXEDDB, // Force WebSQL; same as using setDriver()
name : name,
version : 1.0,
size : 4980736, // Size of database, in bytes. WebSQL-only for now.
storeName : 'YourStoreName', // Should be alphanumeric, with underscores.
description : 'Your Description'
});
instance.setItem("key", {"name":"abc"});
**You can set config from createInstance as well **

Related

How to find out whether default value is used for Datastore ndb property?

Let's say we have a model like this:
class UserConfig(ndb.Model):
name = ndb.StringProperty()
email_subscriber = ndb.BooleanProperty(default=True)
Let's assume email_subscriber was set to default True by mistake and we want to fix that mistake and use default=False instead. I tried changing the value to default=False and that works fine for users created after that code is deployed, but that doesn't fix the problem for existing users.
Is there a way (e.g. some internal property which isn't documented in Datastore documentation), which would provide info whether given prop value was set explicitly by user or using the provided default.
I can write an upgrade which would set email_subscriber=False for all users, but I'm afraid some users might have intentionally checked this property in the app and wouldn't like to break their experience.
tl;dr: How can I determine if value in ndb object was set using the default for that property or was provided explicitly.
There is no way to know this. Once a value is set in the database there is no way to know which part of your code (ndb runs in your code from the database perspective) set the value.

Profile attribute being magically set in Siebel

We have a very weird issue in our Siebel 7.8 application.
In the Application_Start event we define a bunch of profile attributes, which determine if the logged user will be allowed to perform certain operations or not. The code is something like this:
if (userHasSuperpowers) {
TheApplication().SetProfileAttr("CanFly", "Y");
} else {
// CanFly is not set, and GetProfileAttr("CanFly") returns ''
}
Everything works fine, except for one of these profile attributes. The conditions are not met, so we don't set its value. But when we check it using GetProfileAttr... it returns 'Y' instead of ''.
I've checked the code. A lot. I've put traces everywhere, and I'm 100% sure that when the last line of the Application_Start event executes, the attribute is still empty. However, in the first Applet_Load event after the login (in the HLS Salutation Applet (HLS Home) applet), its value has already changed to 'Y'. Why!!? I've looked everywhere, but I can't find anywhere else where we'd be doing a SetProfileAttr. So far, I've ruled out:
Every browser and server script for all our applets, application, BCs and business services.
All the runtime business services (the ones defined directly in the application instead of the SRF).
The Personalization Profile business component fields.
SmartScripts (not that they would matter in this particular scenario, I just mention them to acknowledge that you can set profile attributes there too).
Workflows: every step invoking the SIS OM PMT Service method Set Profile Attribute.
Siebel magically setting its value. The profile attribute name is custom made, in Spanish, and it contains our project name and a row_id. I really don't think Siebel is using the same name for its own profile attributes :).
But wait, there is more, I left the best part for last: the problem only happens in our development environment!
It's not an SRF issue: if we promote the same SRF to our testing or production environments, it works and returns the expected value.
It's not a data problem: still with the same SRF, I can use my local thick client, connecting to our development database with the same login and password, and it works fine too.
It's not a concurrency problem: we are testing with only one user logged in. And even if we had more, they wouldn't share sessions. And even if they did, the value wouldn't be always 'Y'.
It's not a temporary glitch, or something due to a wrong incremental compilation or a corrupted SRF: we have been experiencing this for at least 6 months (obviously, in that time frame, we've had dozens of different SRF files... all of them having the same problem, but only in development, and only if you use the server and not the dedicated client... seriously...).
Where else could I search the profile attribute being set? I've read that they can be persisted to the DB, but in order to do so, you have to define them as a field in a BC based on an S_PARTY extension table, right?
Is there any way to trace profile attribute changes somehow? Maybe rising some loglevel?
How can I find out at least what's being executed after the Application_Start, before loading the first applet?
Any other ideas? I tried checking the SQL spool file too, but didn't find anything suspicious there either (i.e., any of the queries we use to check the conditions, being run twice with different parameters).
Update: following Ranjith R's suggestions, I've also checked:
Other vanilla business services which could be also invoked from a workflow to set a profile attr: User Registration > SetProfileAttr, SessionAccessService > SetProfileAttr and ISS Promotion Agreement Manager > SetProfileAttributes.
Runtime events setting profile attributes directly or using a business service (we don't have any runtime events apart from the vanilla ones).
Business services being called from DVMs (we only have vanilla data validation rules, and none of them apply to our buscomps).
Still no luck...
Ok... finally we found what's happening:
We access the URL to our server and get to the login page. This triggers a first Application_Start event, for the SADMIN user.
We set the profile attributes in that session. SADMIN is the Siebel administrator user, so yes, he hasSuperpowers and therefore we do TheApplication().SetProfileAttr("CanFly", "Y");.
The Application_Start event finishes.
We enter our username and password in the login screen to access into Siebel. This triggers a second Application_Start event, this time for our user. This is the one I was monitoring with the trace files.
We set the profile attributes again in the new session. Our user doesn't hasSuperpowers, so we don't set any value for the CanFly attribute.
The Application_Start event finishes, and CanFly is still empty.
Siebel merges both sessions into one before loading the first screen!! Or at least, it transfers over the profile attributes we had set for SADMIN.
I'm sure it happens that way, for two reasons. First, we changed the profile attribute name to include the username too. And second, instead of storing just an "Y", we are storing now the current date:
var time = (new Date()).getTime();
TheApplication().SetProfileAttr("CanFly_" + TheApplication().LoginName(), time);
We end up having CanFly_SADMIN, but no CanFly_USER, and the time value stored is the same we see in the log file for step 2... which is smaller than any of the values for the *_USER attributes.
So that's what happening. I still don't know why Siebel behaves this way, but that would be matter for another question. According to the Siebel bookshelf:
The Start event is called when the client starts and again when the user interface is first displayed.
...but it doesn't say anythign about it being called from two different sessions, different users too, and then merging them together. It must be something misconfigured in our dev environment, considering it doesn't happen in the other ones.
Does Siebel 7.8 have runtime Events? I can't recall. Runtime events have an action set for setevent, which can set/clear profile attributes.
There are still other vanilla business services which can set profile attributes, try searching in tools flat under business service methods for *rofile*tt*.
The SIS OM service can also be invoked from DVMs for from RunTime events directly, so thats also a possibility.
There is no logging system to see values of Profile Attributes changing, testing is the only way out.

When are Strong Parameters used?

I understand strong parameters are used in cases where we are creating an object and putting it into our database. For example,
User.create(params[:user]) would have to be User.create(params.require(:user).permit(:name, :email, :password).
This is standard and simple to understand, however, are strong parameters required when updating a column or a few attributes in a model?
current_user.update_attributes(params[:user]). Does that have to be current_user.update_attributes(params.require(:user).permit(:name, :email, :password).
Lastly, I don't think it is needed for this case:
current_user.update_column(authentication_token: nil), but would it have needed to be updated if instead we had params = { authentication_token: nil }, and did current_user.update_column(params)?
Any time you pass an instance of ActionController::Parameters to one of the mass assignment apis (new, create, update_attributes, update etc.) you need to permit the appropriate fields.
In a controller the params method returns an instance of ActionController::Parameters, as are any hashes contained within it, so you need to use permit.
If you do
params = { foo: bar }
record.update_attributes(params) or
record.update_attributes(foo: bar)
Then you're passing a normal hash and so you don't need to use permit
Are strong parameters required when updating a column or a few
attributes in a model?
Yes, if the model is being updated with values from the end-user. Never trust the user input.
Suppose the current_user model has a 'role_id' column, which could be 1 for super user, 2 for normal user and 3 for guest. If you don't sanitize the parameters, the end-user could easily forge a request to gain privileges and compromise your application security.
Regarding your last question, you're right. You don't need strong parameters to update the record with values you already know.
I experimented on Rails 4 environment with both update and update_attributes method calls, and I received identical errors for both method calls: ActiveModel::ForbiddenAttributesError in PeopleController#update . In the controller I used #person.update(params[:person]) and #person.update_attributes(params[:person]); so that’s why it says PeopleController in the error message.
Based on the API documentation, it looks like in Rails 4 update_attributes is alias for update. So I guess update_attributes does the same thing as update method in Rails 4:
update_attributes(attributes) public
Alias for ActiveRecord::Persistence#update
Therefore, both update and update_attributes methods have to use strong parameters to update database. I also tested update_attributes method with strong parameters: #person.update_attributes(person_parameters) and it worked
updated
About update_attribute and update_column methods. I just tested those for the very first time through a controller, and with those methods you don’t need to use strong parameters inside of controller ( which was a bit surprise to me), even when you are using params (user provided values). So with update_attribute and update_column methods you can update database without using strong parameters.

Ember-CLI - How to access application classes

Previously when I developed ember application I used App as my global object and every class was stored in this big object.
Like this
window.App.MyModel = Em.Object.extend({});
And in the browser console, I was able to do
App.MyModel.create();
So it was really easy for me to access MyModel class.
Now I started experiments with Ember-CLI, I don't have much experience with this kind of tools. I followed the documentations and I created my model Service like this.
var Service = Ember.Object.extend({});
export default Service
But now, how to access Service class from browser console?
Only way I found was:
App.__container__.resolve('model:service')
But I don't like it much. Is there better way? Btw, can you please explain how export works? Or is there some source (documentation, article) where I can study it?
Thanks a lot for response.
If you're aiming to have something available in most places throughout your application you'll typically want to register it on the container.
There are multiple ways to access the container, and you're correct in that the one you found was less than ideal. The running joke is "any time you directly access __container__ we need to add more underscores."
To directly control how things are registered on the container and injected into the container you typically want to use an initializer. Using initializers in ember-cli is super-easy, they're executed for you automatically.
Checking out the documentation you can see that you get access to the application's container as an argument which allows you to manipulate it in a safe manner.
Once you have access to the container you can use register and inject to make content easily available in particular locations. This was introduced here. One note, accessing things inside of the container from outside the context of your app (browser console) will require the usage of App.__container__ and that is the expected use pattern.
And the export you're running into is an ES6 module system construct, it's not Ember-specific. Playing with the ES6 module transpiler can give you a good sense of what goes in and what comes out in "we can do this today" type of JavaScript.
For ember 3.22, application classes can be accessed like so:
Ember.Namespace.NAMESPACES[1]._applicationInstances.values().next().value.lookup('service:state-events')
Note, you may need to modify the index in NAMESPACES[1] to be something other than 1. You can determine which namespace is your application when this returns true:
Ember.Namespace.NAMESPACES[1] instanceof Application
This approach is how ember-inspector accesses ember applications: https://github.com/emberjs/ember-inspector/blob/50db91b7bd26b12098cae774a307208fe0a47d75/ember_debug/main.js#L163-L168

How can you store a path and transitionTo it later in Ember's v2 Router?

i'm trying to migrate to the new Router in Ember. the use case is this: user is not logged in but requests a URL that requires login. he is redirected to a login route, after successful login he is redirected to his original destination.
i achieved this with the prior Router by overriding Router.route(path) and intercepting path requests when the app was in unauthorized state.
the new Router doesn't have a route() function, also, i don't know how to override it now that the Router instance is created automatically by Ember. i probably shouldn't do that anyway.
there is a Route.redirect() hook that looks useful. however, Route no longer extends Path in the v2 Router, so there is no Root.path, and no path information is passed into Route.redirect(), so i don't know how to save the path info for calling transitionTo() later.
i've supplied my general approach below. how can i accomplish this? it seems like a very common use case for many application.
// i imagine something like this should happen
App.AuthRequiredRoute = Ember.Route.extend({
redirect: function() {
if(!App.controllerFor('login').get('isLoggedIn')) {
var pathToSave = ????
App.controllerFor('login').set('pathAfterLogin',pathToSave);
this.transitionTo('login');
}
}
}
// and then after login, the LoginController would call App.router.transitionTo(this.pathAfterLogin)
I have done a lot of research into this myself in the last day or two. I can share with you what I have discovered, and a couple of thoughts.
First of all, you can some information regarding the current path and contexts like so:
this.router.router.currentHandlerInfos
This returns an array. Each object has both a name and a context property. The names correspond to the name of the router you would call in transitionTo.
In my opinion, although you could work with something like this, it would be messy. The API docs don't refer to this and it may not be the intention to use this as a public API.
I see issues with the above solution for deeper nested dynamic segments too. Given how new the router v2 is, I think it will continue to evolve and a better solution is likely to present itself. It's a fairly common thing to want to save the current location and return to it at a later date.
In the meantime, rather than a redirect, perhaps use a conditional block in your template that presents a login rather than an outlet if the authenticated flag is not set on the ApplicationController? I know it's not as "right" but it is "right now".