I have a Meteor project that is starting to get out of hand with about 800 lines of code. I set out today to modularize and clean things up a bit and things are looking good, but I'm getting some errors that I don't know the best way how to deal with.
Here's a prime example.
I am using d3 to create a force layout (this question isnt specific to d3). I instantiate some variables and most notably
var force = d3.layout.force()
in a file
/client/views/force/forceLayout.js
I made a bunch of controls for this force layout and put them in their own .html and .js files. Heres an example
initChargeSlider = function() {
d3.select("#charge-slider")
.call(d3.slider()
.min(-301)
.max(-1)
.step(5)
.value(Session.get("charge"))
.on("slide", function(evt, value) {
Session.set("charge", value);
// force.stop();
force = force.charge(value);
force.start();
}));
}
Template.charge.rendered = function() {
initChargeSlider();
};
in file
/client/views/force/controls/sliders/charge.js
Due to Meteor loading deeper directories first, I get an error at force = force.charge(value) because forceLayout.js hasn't instantiated force yet.
I'm curious what is the most best way of dealing with this. Moving the files around and loading order is just reversing all the modularizing I just did. I think a singleton or an object or monad may be in order but I'm not sure which or why. I would appreciate an explanation of how to go about fixing these errors.
Thanks
Chet
Meteor before 0.6.5 run files without wrapping them inside a function wrapper (function() { /* your code */ })().
This behavior is still followed if you place your files in client/compatibility folder:
Some JavaScript libraries only work when placed in the
client/compatibility subdirectory. Files in this directory are
executed without being wrapped in a new variable scope. This means
that each top-level var defines a global variable. In addition, these
files are executed before other client-side JavaScript files.
Now, Meteor is more unforgiving of global variables and now one needs to be explicit about declaring them. Hence,
window.force = d3.layout.force()
or even
this.force = d3.layout.force(); // this === window in global context.
would solve the problem.
Related
I support a very old PHP web framework that uses server-side rendering. I decided to implement Vue for the rendering of some modules, so I compiled a hello world app and realized deployment wouldn't be so simple.
The framework works as a giant SPA, with each module being rendered using the html output of a body() function. The output is replaced in the client's DOM without reloading the page itself.
<script> tags are banned for security reasons and will be sanitized from the resulting html. The only way to deliver JS to the client is by using an eval_js() function.
The problem is rather simple. I need to safely load JS code several times in the same DOM. I cannot load it as-is after app compilation, because from the second time onwards the code is executed (every time a user visits a module, or performs an action) the code will attempt to re-define global variables and kill the whole client.
The solution is also rather simple, just rewrite the JS code such that every global definition is transformed into a window property. This way, even if the same piece of code gets executed several times in the same DOM, it will simply replace window properties rather than attempting to re-define variables.
In example, the following input:
function Yr(t){
const b = t.prototype.hasOwnProperty;
this._init(b);
}
var hOe = sg(uOe, fOe, dOe, !1, null, "e687eb20", null, null);
const vOe = {
name: "AmmFilters",
components: {
AmmOptionSelect: pOe
}
};
new Yr({...}).$mount("#app");
Would be rewritten into:
window.Yr = function(t){
const b = t.prototype.hasOwnProperty;
this._init(b);
}
window.hOe = sg(window.uOe, window.fOe, window.dOe, !1, null, "e687eb20", null, null);
window.vOe = {
name: "AmmFilters",
components: {
AmmOptionSelect: window.pOe
}
}
new window.Yr({...}).$mount("#app");
I initially considered to write my own parser, but then realized that ES6+ syntax is no child's play. The code I will attempt to rewrite is optimized & obfuscated which means it will have all sort of complex syntax and I must be careful not to turn scoped definitions into window properties.
Any ideas on a tool that already performs this task? The resulting JS code should have no difference from the original, as global scoped variables end up in the window object anyway.
I believe it would be a fairly useful tool for various use cases, so thought about asking before attempting to reinvent the wheel.
I'm currently running a heavy computation (i.e. generating a Monte Carlo tree), which is an expensive operation. I only have a few seconds to build as big of a tree as I can, so I am using subprocesses in Node.js in order to build multiple trees, and then aggregate their data together to make a more informed decision.
I understand that subprocesses do not share information/memory, and I need to use modules within these subprocesses that are located in a file, called "Epilog.js" on my machine.
When I run functions that are in epilog.js from the main file, it works just fine. But all of my functions that are in my worker threads return absolutely nothing.
I have tested to make sure that the parameters of the functions I am trying to use in "epilog.js" aren't empty, and they're not. The problem isn't in the parameter.
I have also tested to see what happens if I simply don't import, and instead of just outputting an undefined array, I get an error saying that there is no function called "findroles".
//My main thread.
var fs = require('fs');
eval(fs.readFileSync('epilog.js') + '');
var process = fork('./buildGraph.js');
process.send({library});
//My worker thread.
//buildGraph.js
var fs = require('fs');
eval(fs.readFileSync('epilog.js') + '');
// receive message from master process
process.on('message', async(message) => {
library = message["library"];
console.log(findroles(library));
// findroles(library) is a function that is defined in epilog.js,
//and this outputs an array of "roles" given a parameter,library.
// For some reason this function outputs [], rather than giving me
// all of the roles. If I run this exact line from my main thread,
// it doesn't give any errors and outputs the right array:
// e.g. ['red', 'white'].
});
I expect to get not the empty array, but [red, white], as I do if I were to run the same line in the main thread. Does anyone have an idea as to the inconsistency of the functions? I'm very new to node.js and this isn't a class focused too much on software engineering in JavaScript, so I'd appreciate if someone can dumb down what is going on, as this is all very new to me.
If your script does not find the function called findroles then there is a problem with the importing method. Using the eval function for importing is not the normal way of importing modules. Try something like this:
// buildGraph.js
const epilog = require("./epilog.js");
......
console.log(epilog.findroles(library));
then epilog.js
exports.findroles = function (library) {
// function content
}
You can find more info here:
https://www.w3schools.com/nodejs/nodejs_modules.asp
Base on the document and example here, everything seem correct but I think the problem come from this line:
var process = fork('./buildGraph.js');
you might override the original process.
try to change it to
const n = fork('./buildGraph.js');
So I'm working with an enterprise tool where we have javascript scripts embedded throughout. These scripts have access to certain built-in objects.
Unfortunately, the tool doesn't give any good way to unit test these scripts. So my thinking was to maintain the scripts in a repo, mock the built-in objects, and then set up unit tests that run on my system.
I'm pretty ignorant to how JavaScript works in terms of building, class loading, etc. but I've been just trying things and seeing what works. I started by trying out Mocha by making it a node project (even though it's just a directory full of scripts, not a real node project). The default test works, but when I try and test functions from my code, I get compiler errors.
Here's what a sample script from my project looks like. I'm hoping to test the functions, not the entire script:
var thing = builtInObject.foo();
doStuff(thing);
doMoreStuff(thing);
function doStuff(thing) {
// Code
}
function doMoreStuff(thing) {
// More Code
}
Here's what a test file looks like:
var assert = require('assert');
var sampleScript = require('../scripts/sampleScript.js');
describe('SampleScript', function() {
describe('#doStuff()', function() {
it('should do stuff', function() {
assert.equal(-1, sampleScript.doStuff("input"));
});
});
});
Problem happens when I import ("require") the script. I get compilation errors, because it doesn't builtInObject. Is there any way I can "inject" those built in objects with mocks? So I define variables and functions that those objects contain, and the compiler knows what they are?
I'm open to alternative frameworks or ideas. Sorry for my ignorance, I'm not really a javascript guy. And I know this is a bit hacky, but it seems like the best option since I'm not getting out of the enterprise tool.
So if I get it right you want to do the unit tests for the frontened file in the Node.js environment.
There are some complications.
First, in terms of Node.js each file has it's own scope so the variables defined inside of the file won't be accessible even if you required the file. So you need to export the vars to use them.
module.exports.doStuff = doStuff; //in the end of sample script
Second, you you start using things like require/module.exports on the frontend they'll be undefined so you'll get an error.
The easiest way to run your code would be. Inside the sample script:
var isNode = typeof module !== 'undefined' && module.exports;
if (isNode) {
//So we are exporting only when we are running in Node env.
//After this doStuff and doMoreStuff will be avail. in the test
module.exports.doStuff = doStuff;
module.exports.doMoreStuff = doMoreStuff;
}
What for the builtInObject. The easies way to mock it would be inside the test before the require do the following:
global.builtInObject = {
foo: function () { return 'thing'; }
};
The test just passed for me. See the sources.
Global variables are not good anyway. But in this case seems you cannot avoid using them.
Or you can avoid using Node.js by configuring something like Karma. It physically launches browser and runs the tests in it. :)
I am writing an application in Titanium for Android. I have a lot of code in a single JS file. I would like to know if there is any function like php's include to break the code into multiple files and then just include them.
Thanks
Use the CommonJS / RequireJS approach, specifically the require command. This is the (strongly) recommended way for dealing with large systems in Titanium, and is well documented on their website, along with many best practices for dealing with JavaScript modularization specific to Titanium. Here is the documentation from Titanium on this.
For example, to create a module that encapsulates a 'view' of some kind, put it in a file named MyCustomView.js with this content:
// MyCustomView.js
function MyCustomView(message) {
var self = Ti.UI.createView({
backgroundColor : 'red'
});
var label = Ti.UI.createLabel({
text : message,
top : 15,
.... // Other initialization
});
// ... Other initialization for your custom view, event listeners etc.
return self;
}
module.exports = MyCustomView;
Now you can easily use this module in another class, lets assume you put this in your /Resources folder, lets load the module inside app.js and add it to the main window.
// app.js
var MyCustomView = require('MyCustomView');
var myView = new MyCustomView('A message!');
Titanium.UI.currentWindow.add(myView);
You can use this approach to make custom views, libraries of reusable code, and anything else you would like. Another common thing would be to have a Utility class that has many different helper functions:
// Utility.js
exports.cleanString = function(string) {
// Replace evil characters
var ret = string.replace(/[|&;$%#"<>()+,]/g, "");
// replace double single quotes
return ret.replace(/"/g, "''");
}
This method can be easily used like this:
// app.js
var Utility = require('Utility.js');
Ti.API.info(Utility.cleanString('He##o W&orld$'));
Another common method I use it for is to implement the Singleton pattern as each module loaded is its own functional context, so if you like, you can have values that persist:
// ManagerSingleton.js
var SpriteManager = {
count : 0
};
exports.addSprite = function() {
SpriteManager.count++;
}
exports.removeSprite = function() {
SpriteManager.count--;
}
You would load and use this much the same way as Utility:
// app.js
var ManagerSingleton = require('ManagerSingleton');
ManagerSingleton.addSprite();
This is something of a more elegant solution instead of using global variables. These methods are by no means perfect, but they have saved me a lot of time and frustration, and added depth, elegance, and readability to my Titanium code for Apps of all sizes and types.
There are two dominant module systems in the Javascript world.
One is the CommonJS and the second is AMD. There is a lot of discussion about which one is best and for what purpose. CommonJS is more widely used for server side JS while AMD is used mostly for browser JS.
RequireJS (requirejs.org) seems to be the most popular AMD system.
For information on JS module systems please read here: http://addyosmani.com/writing-modular-js/
I've created a Dojo module which depends on dojox/data/JsonRestStore like this:
define("my/MyRestStore",
["dojo/_base/declare", "dojox/data/JsonRestStore"],
function(declare, JsonRestStore) {
var x = new JsonRestStore({
target: '/items',
identifier: 'id'
});
...
which is fine. But now I want to have the the uncompressed version of the JsonRestStore code loaded so that I can debug it. I can't find any documentation on how to do this, but since there is a file called 'JsonRestStore.js.uncompressed.js' I changed my code to:
define("my/MyRestStore",
["dojo/_base/declare", "dojox/data/JsonRestStore.js.uncompressed"],
function(declare, JsonRestStore) {
...
thinking that might work.
I can see the JsonRestStore.js.uncompressed.js file being loaded in FireBug, but I get an error when trying to do new JsonRestStore:
JsonRestStore is not a constructor
Should this work?
Is there a way of configuring Dojo to use uncompressed versions of all modules? That's what I really want, but will settle for doing it on a per dependency basis if that's the only way.
Update
I've found a way to achieve what I want to do: rename the JsonRestStore.js.uncompressed.js file to JsonRestStore.js.
However, this seems a bit like a hacky workaround so I'd still be keen to know if there is a better way (e.g. via configuration).
You have two options
1) Create a custom build. The custom build will output a single uncompressed file that you can use for debugging. Think the dojo.js.uncompressed.js but it includes all the extra modules that you use.
OR
2) For a development environment, use the dojo source code. This means downloading the Dojo Toolkit SDK and referencing dojo.js from that in the development environment.
For the projects I work on, I do both. I set up the Dojo configuration so that it can be dynamic and I can change which configuration that I want using a query string parameter.
When I am debugging a problem, I will use the first option just to let me step through code and see what is going on. I use the second option when I am writing some significant js and don't want the overhead of the custom build to see my changes.
I describe this a bit more at
http://swingingcode.blogspot.com/2012/03/dojo-configurations.html
I think the reason for this is due to the fact that the loader declares its class-loads (modules), by the file conventions used. The 1.7 loader is not too robust just yet, ive had similar problems until realizing how to separate the '.' and '/' chars.
Its only a qualified guess; but i believe it has to do with the interpretation of '.' character in the class-name which signifies as a sub-namespace and not module name.
The 'define(/ * BLANK * / [ / * DEPENDENCIES * / ], ...)' - where no first string parameter is given - gets loaded by the filename (basename). The returned declare also has a saying though. So, for your example with jsonrest, its split/parsed as such:
toplevel = dojox
mid = data
modulename = JsonRestStore.js.uncompressed
(Fail.. Module renders as dojox.data.JsonRestStore.js.uncompressed, not dojox.data.JsonRestStore as should).
So, three options;
Load uncomressed classes through <script src="{{dataUrl}}/dojox/data/JsonRestStore.js.uncompressed.js"></script> and work them on dojo.ready
I think modifying the define([], function(){}) in uncompressed.js to define("JsonRestStore", [], function() {}) would do the trick (uncomfirmed)
Use the dojo/text loader, see below
Text filler needed :)
define("my/MyRestStore",
["dojo/_base/declare", "dojo/text!dojox/data/JsonRestStore.js.uncompressed.js"],
function(declare, JsonRestStore) {
...
JsonRestStore = eval(JsonRestStore);
// not 100% sure 'define' returns reference to actual class,
// if above renders invalid, try access through global reference, such as
// dojox.dat...