Passing Chrome FileSystem API FileEntry to Web worker - google-chrome-app

I'm trying to use a WebWorker to parse certain files from a directory the user selected, like so:
function readEntries(reader, callback) {
reader.readEntries(function(results) {
results.forEach(function(entry) {
if (entry.isDirectory) { readEntries(entry.createReader(), callback); }
else { callback(entry); }
});
});
}
readEntries(initialDir.createReader(), function(entry) {
var nodeWorker = new Worker('worker.js');
nodeWorker.onmessage = function(e) { /* do stuff */ }
nodeWorker.postMessage(entry);
});
The initialDir is a DirectoryEntry from chrome.fileSystem.chooseEntry({'type': 'openDirectory'}, ....
The corresponding worker.js would use FileReaderSync to parse the passed file(s) and pass analysis results back to /* do stuff */.
If I execute this, I get the following DOMException:
Error: Failed to execute 'postMessage' on 'Worker': An object could not be cloned.
Since entry.toURL() returns an empty string, how am I supposed to pass local, unsandboxed files to the web worker? Is there any other way to achieve what I want?

Related

Can I prevent Babel from traversing code inserted by a plugin?

I'm building a plugin that inserts enterFunction() in front of every existing function call by calling path.insertBefore. So my code is transformed from:
myFunction();
To:
enterFunction();
myFunction();
The problem is that when I insert the node Babel once again traverses the inserted node. Here's the logging output:
'CallExpression', 'myFunction'
'CallExpression', 'enterFunction'
How can I prevent Babel from entering the enterFunction call expression and its children?
This is the code I'm currently using for my Babel plugin:
function(babel) {
return {
visitor: {
CallExpression: function(path) {
console.log("CallExpression", path.node.callee.name)
if (path.node.ignore) {
return;
}
path.node.ignore = true
var enterCall = babel.types.callExpression(
babel.types.identifier("enterFunction"), []
)
enterCall.ignore = true;
path.insertBefore(enterCall)
}
}
}
}
The Babel Handbook mentions the following section:
If your plugin needs to not run in a certain situation, the simpliest thing to do is to write an early return.
BinaryExpression(path) {
if (path.node.operator !== '**') return;
}
If you are doing a sub-traversal in a top level path, you can use 2 provided API methods:
path.skip() skips traversing the children of the current path. path.stop() stops traversal entirely.
outerPath.traverse({
Function(innerPath) {
innerPath.skip(); // if checking the children is irrelevant
},
ReferencedIdentifier(innerPath, state) {
state.iife = true;
innerPath.stop(); // if you want to save some state and then stop traversal, or deopt
}
});
In short, use path.skip() to skip traversing the children of the current path.
One application of this method is illustrated in this snippet using Visitors, CallExpression and skip():
export default function (babel) {
const { types: t } = babel;
return {
name: "ast-transform", // not required
visitor: {
CallExpression(path) {
path.replaceWith(t.blockStatement([
t.expressionStatement(t.yieldExpression(path.node))
]));
path.skip();
}
}
};
}

Unable to Save ParseObject with User ACL in Cloud Code

I have an issue saving changes to an object from a Cloud Code function.
I have a collection called Character and one record inside it.
This Character record has an ACL with Public Read, and Private Write Access by a specific ParseUser (6MwfSLdAxd).
In Unity, I authenticated the user and I then call the Cloud Code function as follows:
ParseCloud.CallFunctionAsync<Character>("startBattle", null).ContinueWith(t =>
{
Debug.Log("I got here...");
Debug.Log(t.Result.ClassName);
});
In my Cloud Code function, I grab the first character in the collection (ignoring checking if it belongs to this user, because at the moment there is only one and it DOES belong to this user - there's only one user too).
var Character = Parse.Object.extend("Character");
Parse.Cloud.define("startBattle", function (request, response) {
var user = request.user;
if (user == null)
{
return response.error("You must login before you can battle!");
}
var characterQuery = new Parse.Query(Character);
characterQuery.first()
.then(
function (character) {
character.set("name", "Cloud Code sucka");
character.save().then(function(character) {
return response.success(character);
});
},
function (error) {
return response.error("You must create a character before you can battle! " + error);
}
)
});
However, I simply cannot save any changes to this character. All the documentation and forum posts I've found suggest that if you call a Cloud Code function when authenticated then that function should have the same level permissions as the user calling it.
The only time this code works is if I set the ACL of the character to Public Write.
Does anyone have any ideas why this wouldn't be working?
Note: Worth noting that I can see in the server logs that the Cloud Code function IS being called by the authenticated user 6MwfSLdAxd as I get this error (if I add a response.error call):
error: Failed running cloud function startBattle for user 6MwfSLdAxd with:
Input: {}
Error: {"code":141,"message":"Messed up: [object Object]"} functionName=startBattle, code=141, message=Messed up: [object Object], , user=6MwfSLdAxd
error: Error generating response. ParseError { code: 141, message: 'Messed up: [object Object]' } code=141, message=Messed up: [object Object]
[object Object]
[object Object]
After some extensive searching I've now found the solution to this.
For anyone else encountering the same issues, you should be aware that whilst Parse.com used to run Cloud Code functions in the context of the user that called them (afaik), self-hosted Parse Servers do not.
In order to call queries or saves in the context of a user you must pass their session token as shown below. I hope this saves someone the hours of confusion I went through!
var MyObject = Parse.Object.extend("MyObject");
Parse.Cloud.define("myCloudFunction", function (request, response) {
var user = request.user;
var sessionToken = user.getSessionToken();
var query = new Parse.Query(MyObject)
.find({ sessionToken: sessionToken })
.then(
function (object) {
object.set("someKey", "someValue");
return object.save(null, { sessionToken: sessionToken });
}
)
.then(
function (object) {
return response.success(object);
},
function (error) {
return response.error(error.message);
}
);
});
For further context see:
https://github.com/ParsePlatform/parse-server/wiki/Compatibility-with-Hosted-Parse#cloud-code

how to resolve optional url path using ng-resource

There are restful APIs, for instance:
/players - to get list for all players
/players{/playerName} - to get info for specific player
and I already have a function using ng-resource like:
function Play() {
return $resource('/players');
}
Can I reuse this function for specific player like:
function Play(name) {
return $resource('/players/:name', {
name: name
});
}
so I want to...
send request for /players if I didn't pass name parameter.
send request for /players/someone if I passed name parameter with someone
Otherwise, I have to write another function for specific play?
Using ngResource it's very, very simple (it's basically a two-liner). You don't need even need to create any custom actions here*.
I've posted a working Plunkr here (just open Chrome Developer tools and go to the Network tab to see the results).
Service body:
return $resource('/users/:id/:name', { id:'#id', name: '#name' })
Controller:
function( $scope, Users ){
Users.query(); // GET /users (expects an array)
Users.get({id:2}); // GET /users/2
Users.get({name:'Joe'}); // GET /users/Joe
}
of course, you could, if you really wanted to :)
This is how I did it. This way you don't have to write a custom resource function for each one of your endpoints, you just add it to your list resources list. I defined a list of the endpoints I wanted to use like this.
var constants = {
"serverAddress": "foobar.com/",
"resources": {
"Foo": {
"endpoint": "foo"
},
"Bar": {
"endpoint": "bar"
}
}
}
Then created resources out of each one of them like this.
var service = angular.module('app.services', ['ngResource']);
var resourceObjects = constants.resources;
for (var resourceName in resourceObjects) {
if (resourceObjects.hasOwnProperty(resourceName)) {
addResourceFactoryToService(service, resourceName, resourceObjects[resourceName].endpoint);
}
}
function addResourceFactoryToService (service, resourceName, resourceEndpoint) {
service.factory(resourceName, function($resource) {
return $resource(
constants.serverAddress + resourceEndpoint + '/:id',
{
id: '#id',
},
{
update: {
method: 'PUT',
params: {id: '#id'}
},
}
);
});
}
The nice thing about this is that it takes 2 seconds to add a new endpoint, and I even threw in a put method for you. Then you can inject any of your resources into your controllers like this.
.controller('homeCtrl', function($scope, Foo, Bar) {
$scope.foo = Foo.query();
$scope.bar = Bar.get({id:4});
}
Use Play.query() to find all players
Use Play.get({name:$scope.name}) to find one player

AngularJS ng-repeat with data from service

Originally in my app, I created controllers with very basic $http calls to get a resource by getting the ID of an object from the url ($routeParams). Ng-repeat display the results correctly.
However, I noticed refreshing in a later view (different controller) wiped out the data and broke the page. So, I created a function on the service to be used in multiple controllers, to check whether the data has is available and to react as follows:
1) If the resource is defined, return it (no API call)
2) If the resource is not defined, get the id from the url and get it from the API
3) If the resource is not defined & you can't get the ID, just return false.
However, this broke the code: the template rendered before the service returned the data, and ng-repeat did not update. The code looks like this:
angular.module('myApp', ['ngCookies'])
.config(...)
.service('myService', ['$cookies', '$http', function($cookies, $http) {
myData = {};
return {
getData:function(dataID) {
if(myData.name) {return myData);
else if (dataID && dataID !== '') {
$http.get('/api/data/' + dataID)
.success(function(data) {
myData = data.object;
$cookies.dataID = data.object.id;
return myData;
}
}
else { return false; }
}
}
}]);
function myCtrl($scope, $http, $routeParams, myService) {
$scope.data = myService.getData($routeParams.dataID);
...
}
And here's the template. It's in jade, which means rather than angle brackets, you just list the element with parameters in parenthesis right after, and content after the parenthesis.
h2 My heading
ul
li(ng-repeat='option in data')
a(href="#", ng-click='someFuncInCtrl(option.name)') {{ option.name }}
When the controller did the $http.get itself, the ng-repeat worked fine because the $scope was updated in the ".success" callback. Now that there's a service that returns the data after a slight delay, "$scope.data" is just undefined, the ng-repeat list is empty.
I used a console.log to check myData right before return "return myData", and the myData is working, it just isn't returned in time, and for whatever reason the list is not updating whenever $scope does get the data.
I looked a using $routeProvider's resolve... but that makes getting the ID from the url challenging, as the resolve object doesn't seem to have access to $routeParams. I know that $scope.$apply is supposed to help update the scope when it's altered by outside functions... but I have no clue where to put it. The most similar problem on SO didn't use a service.
I tried:
$scope.$apply($scope.data = myService.getData($routeParams.dataID));
And
$scope.$apply(function() {
$scope.data = myService($routeParams.dataID);
});
Both times I only got Error: $digest already in progress.
The problem is on the way you interact with the service. Since your getData function can return both synchronous and/or asynchronous information, you can't just use normal return(s).
$http.get('/api/data/' + dataID)
.success(function(data) {
myData = data.object;
$cookies.dataID = data.object.id;
return myData;
});
The return on the above snippet will not return anything from getData because it will be executed on the context of the $http.get success callback (and not on the getData call stack).
The best approach for handling sync and async service requests is to use promises.
Your getData function should look something like this:
getData:function(dataID) {
var deferred = $q.defer();
if(myData.name) {
deferred.resolve(myData);
} else if (dataID && dataID !== '') {
$http.get('/api/data/' + dataID)
.success(function(data) {
myData = data.object;
$cookies.dataID = data.object.id;
deferred.resolve(myData);
// update angular's scopes
$rootScope.$$phase || $rootScope.$apply();
});
} else {
deferred.reject();
}
return deferred.promise;
}
Note: You need to inject the $rootScope on your service.
And on your controller:
function myCtrl($scope, $http, $routeParams, myService) {
myService.getData($routeParams.dataID).then(function(data) {
// request was successful
$scope.data = data;
}, function() {
// request failed (same as your 'return false')
$scope.data = undefined;
});
}

require.js synchronous loading

I'd like to define a module which computes a new dependancy, fetches it and then returns the result. Like so:
define(['defaults', 'get_config_name'], function(defaults, get_config_name) {
var name = get_config_name();
var config;
require.synchronous([configs / '+name'], function(a) {
config = defaults.extend(a);
});
return config;
});
Is there a way to do this or a better way to attack this problem?
You may try to use synchronous RequireJS call require('configs/'+get_config_name()), but it will load a module synchronously only if it is already loaded, otherwise it will throw an exception. Loading module/JavaScript file synchronously is technically impossible.
UPD: It's possible (see Henrique's answer) but highly unrecommended. It blocks JavaScript execution that causes to freezing of the entire page. So, RequireJS doesn't support it.
From your use case it seems that you don't need synchronous RequireJS, you need to return result asynchronously.
AMD pattern allows to define dependencies and load them asynchronously, but module's factory function must return result synchronously. The solution may be in using loader plugin (details here and here):
// config_loader.js
define(['defaults', 'get_config_name'], function(defaults, get_config_name) {
return {
load: function (resourceId, require, load) {
var config_name = 'configs/' + get_config_name();
require([config_name], function(config) {
load(defaults.extend(config));
})
}
}
});
// application.js
define(['config_loader!'], function(config) {
// code using config
});
If get_config_name() contains simple logic and doesn't depend on another modules, the better and simpler is calculating on the fly paths configuration option, or in case your config depends on context - map configuration option.
function get_config_name() {
// do something
}
require.config({
paths: {
'config': 'configs/' + get_config_name()
}
});
require(['application', 'defaults', 'config'], function(application, defaults, config) {
config = defaults.extend(config);
application.start(config);
});
Loading JavaScript synchronously is NOT technically impossible.
function loadJS(file){
var js = $.ajax({ type: "GET", url: file, async: false }).responseText; //No need to append
}
console.log('Test is loading...');
loadJS('test.js');
console.log('Test was loaded:', window.loadedModule); //loadedModule come from test.js