I have been trying to figure out the best way to write GAS libraries for a while now but I have a hart time figuring it out. I read Douglas Crockford's - Javascript: The good parts and I'm trying to implement these lessons in GAS. Every imported library adds global variable to your project (of the ScriptModule type), so the modular design pattern seems like a good place to start. Borrowing from the article I linked to such a pattern might look like this:
var MODULE = (function () {
var my = {},
privateVariable = 1;
function privateMethod() {
// ...
}
my.moduleProperty = 1;
my.moduleMethod = function () {
// ...
};
return my;
}());
This module could then be called like this:
var module = LibName.MODULE;
var property = module.moduleProperty; // 1
var method = module.moduleMethod; // ...
From what I gather it is best have as few global variables as possible, so the general advice seems to be to save everything in one global variable. So the naming convention should then look something like this: LibName.PROJECT_NAME, where project name is the name of your single global variable which holds a module with everything else.
My goal here is to design secure, non-conflicting libraries. Am I right to use this design pattern? Has anyone developed their own robust design patterns for GAS libraries yet?
When you import a script as a library in GAS a new "namespace" is already created for it, so there's no need or gain in creating another one yourself. You'd have have to "deference" it, like you did:
//there's no purpose for this
var module = LibName.MODULE;
var method = module.method;
//comparing if you write code directly on the library
var method1 = LibName.method1;
GAS is not client-side javascript, most of things you learn don't really apply to Apps Script, e.g. there's no DOM, no namespace conflict, no browser compatibility issues, etc.
By the way, I don't think this object nesting structure even works on Apps Script libraries.
Related
So, I am working on an MVVM-based core SDK for use any time I am developing some Google Apps Script based software, called OpenSourceSDK. It contain core business logic, including base classes to extend. For example, the file Models/BaseModel.gs in it is defined to be:
class BaseModel {
static FromObject(obj) {
let model = new this()
return model.unmarshal(obj)
}
/**
* Creates a deep-copy of this, as plain Object
*/
marshal() {
return JSON.parse(JSON.stringify(this));
}
/**
* Writes the target object into this and returns this
* #param {*} obj
*/
unmarshal(obj) {
return Object.assign(this, obj);
}
}
I follow the instructions to create this library, and make sure to import it into the project that's using it. When I attempt to use it, things go sideways: I am able to access the functions, but not the classes nor the constants!! I start typing in OpenSourceSDK.BaseModel, and IntelliSense is like "idk what to do". I sanity test via Logger.log(typeof OpenSourceSDK.BaseModel), only to get undefined.
This is unacceptable.
What is the most efficient way to make sure these classes (and constants) are shared from that library?
NOTE: I do not accept the following "solutions":
factory method per class. Why not? Because:
maintenance nightmare. Imagine making any change to the constructor signature of the class. You'd have to update the factory function on every change!
extra boilerplate that is wrapper around something that is so simple.
what happen if we want to extend a base class coming from the library?
going all function-based. That defeats the whole purpose of this project. There's a reason I am doing this MVVM, and hence OOP, style.
In ES6 let, const, and class do not add themselves to the global variable (unlike var and function).
My favorite way of exporting them on Apps Script is to assign all exported objects to globalThis:
class BaseModel { /* [...] */ }
class AnotherType { /* [...] */ }
const ExampleConstant = 420
Object.assign(globalThis, {
BaseModel,
AnotherType,
ExampleConstant,
})
This allows you to choose what to "export" and follows a nice enough syntax.
Update
You can also mimic the individual export syntax with the following function:
function export_(exported) {
const { name } = exported
if (!name)
throw new Error(`To export a function or a class, it needs to have a name.`)
if (name in globalThis)
throw new Error(`A feature with the same name already exists on globalThis.`)
globalThis[name] = exported
return exported
}
And you use it like this:
export_ (class Example {
// [...]
})
Unfortunately the final underscore is necessary to not clash with the keyword and the parenthesis are not my cup of tea (specially the one at the end). But I agree that this would be more maintainable as you don't need to manually update the list.
References
globalThis (MDN)
Object.assign() (MDN)
I was able to get it resolved, but the solution is...hacky.
So, apparently, Google Apps Script exports only what is in globalThis of a project: just the functions and variables. No classes, no constants, ...
Probably has a lot to do with how ES6 works, with its globalThis behavior. One can see that in action, by creating a dummy function, a dummy variable, and a dummy class in their local developer console:
function aFunction() { return 42; }
class AClass {}
var a = 41;
When they log globalThis, they'll find some massive object with a, aFunction in it, but no class:
Also, it turns out that globalThis is immutable, so you can't hack the class in via some IIFE...
The only recourse is to go prepend var [ClassName] = in front of every class that is to be made public. That would assign the [ClassName] to a publicly exposed var.
I can't wait til Google Apps Script comes up with a better way to expose classes!!
I recently came across this issue. For a project I'm working on, we were using .bind() way too often and it actually hit the performance quite hard considering that we only have 16ms for the rendering loop to do things.
So I did some jsperf and noticed that calling a bound function (besides of the extra garbage) is way slower than calling an unbound function or using .call on a function.
I literally changed every piece of code to avoid bindings and to use .call/.apply instead. Ding this i not only spawned less functions but also increased the performance of my app a great deal.
However, I was unsatisfied with this and wrote a new way of binding functions.
https://github.com/SebastianNette/FastBind
This is overwriting the native bind method with a .call/.apply approach.
And it runs 96% faster.
Doing some testings on nodejs is came to these results:
Calling a bound function is 20 times slower than calling an unbound function.
Calling a bound function with my own approach takes only 2 times the time of the unbound call.
So I was wondering what is wrong with the native binding function. Why does it behave like that? And which would be the best way to deal with that issue.
Most of my app code is now written like that:
var scope = this;
this.boundFn = function(a,b,c) { return scope.fn(a,b,c); };
Or even
this.callback = fn;
this.context = context;
this.callback.call(this.context);
I do prefer the latter because it doesn't spawn any new functions. However, sometimes I just do have to bind. (handlers, timers, etc).
My educated guess is that it makes a clone of the object you are using but replaces the underlying prototype of object. Instead of using a generic precompiled object from the page rendered code it now has to take two things:
The passed variable thats to be come this. analyse it, clone it. then inject the specified function thats to be called into the new object. Then execute the function in the new object. afterwards if no longer called clean it up.
The more complex and more scoping loops an object has the long the bind will take because the engine needs to traverse the scope tree of all functions and parameters to see what needs to be copied.
You are already using scoping, which I strongly advice. It is less memory intense and the engine does not have to copy the objects and then call the functions. And you get the added benefit that you can access properties from both objects.
In my experience binding is never truly needed. Just use setters and getters for properties, otherwise the scoped variables won't always change in the main object.
Take for example this snippet
function domagic() {
this.myproperty = "Hello ";
}
domagic.prototype = {
perform:function(){
var that = this;
var hello = "World";
setTimeout(function(){
// this in this contect is whatever runs timeout. not domagic
// I use this for jQuery and my own objects to get best
// of both worlds, but I always post a comment in a scope
// to remind myself what this and that refers to.
window.alert(that.myproperty+hello);
that.set("Goodbye ");
},2000);
},
set : function(what) {
this.myproperty = what;
}
};
magic = new domagic();
magic.perform();
setTimeout(function(){magic.perform();},2000);
I have recently picked up flixel (I have programmed before, but I have not in a while) and I have come across a problem. I am attempting to create maps, and eventually there will be multiple maps available.
I currently have a .txt file that has information that eventually goes into an array. Then I go from array to map with loadmap. It is maybe a simple way to accomplish this task, and maybe their are better ways (I have not explored all the possibilities with flixel, and if there are any opinions, go ahead and let me know) but it works good for now.
As I have previously said, I am trying to do this with multiple maps. I could do this by using [Embed(source = "")] for each .txt file, but this may end up being annoying. So, here is my question: Is there a possible way Embed a file based upon a variable?
My Map class looks like this:
public function Map(MapSet:String, TileSet:String)
{
super(MapSet, TileSet);
//more stuff
}
Now I have tried:
[Embed(scource="data/MapSets/" + MapSet + ".txt", mimeType = "application/octet-stream")]private var loadedMap:Class
and then I use:
map = new Map("Map1x1", "ForestTiles");
add(map);
Is there a possibility of doing this in a different way? Or maybe I am doing something wrong? All opinions are welcome.
It's beneficial to know what code does when using it.
Embed is a meta tag. It tells the compiler to include a certain file into the .swf file.
That means this does not happen at runtime.
When this embed code is "executed", your variables don't even exist yet.
That's why your code cannot work.
Despite not working, your solution is still valid:
If you find it tedious to generate code, write a program that does this for you. Create/use a program that finds all valid files in the given directory and creates all the embed tags. Run this program before the compiler.
To embed a text file and use as a string, try this:
// create source var TextSource
[Embed(source="textFile.txt",mimeType="application/octet-stream")]
private var TextSource:Class;
var myByteArray:ByteArray = new TextSource();
var myString:String = myByteArray.readUTFBytes(myByteArray.length);
// then use for your function
map = new Map("Map1x1", myString);
I've created a library class file in my CakePHP 2.0 app. It's a single PHP class called emailManager Which exists within a folder emailManager within CakePHP's libaray folder. I would love to know what is the simplest way to reference the database from this library class.
I would love to be able to do something like $this->AppModel->query("SELECT * FROM some_table_in_my_db"), that way I do not have to track DB configurations in separate places, but I'm not sure how to achieve this.
Also, I feel it is important to mention that the tables I am working with do not adhere to CakePHP table naming convention. They predate our use of CakePHP and so I cannot change my tables to fit CakePHP's model format. this is why I want generic database access via something like query
EDIT: I have constructed a temporary solution, but I know a better one is possible.
I have a model in my cake app called MySimpleConstuct and then in the library file I include the MySimpleConstruct Model as followed:
// import this model
$this->GivenModel = ClassRegistry::init('MySimpleConstruct');
$this->GivenModel = new MySimpleConstruct();
// Then it is possible to do as followed:
$table_data = $this->GivenModel->query('SELECT * FROM like_some_table_dude' WHERE 1);
This is not ideal so I still searching for a better solution.
Any help would be greatly appreciated.
#John Galt, I suppose it's not an exact duplicate but it is very similar and the resolution does appear to apply to your situation very directly.
The other technique you could consider using would be to instantiate the Library in the controller and than give it a reference of the model.
class TestController extends AppController {
function index(){
App::uses('TheLibrary', 'Lib');
$obj = new TheLibrary();
$obj->GivenModel = &$this->GivenModel;
}
}
-EDIT-
And then within the library you've written do something like this.
class TheLibrary {
var $GivenModel = null;
function some_query(){
return $this->GivenModel->query('SELECT * FROM like_some_table_dude WHERE 1');
}
}
The first code snippet is of the Controller instantiating your library and then giving the library a reference to the Model as the property GivenModel. The "&" symbol makes the assignment a reference (see How does the '&' symbol in PHP affect the outcome?). The second code snippet is of a sample of how the library would use that property.
I do understand that you are trying to use a model from the library and that is what the solution you have in your edit and my proposed solution both do. However I will note again that this is not proper MVC convention and you should reconsider how you are using Libraries.
Mootools classes have an initialize() method that's called when a new object is instantiated.
It seems that setup() is a commonly used method as well.
Most classes I've observed call this.setup() from initialize() and nowhere else, which has left me wondering:
What's the purpose of setup()? Why not just put the setup() code in initialize()? When does it make sense to use a setup() method?
as oskar says, there's no benefit to using that very name. If you wanted to, you could only use a single initialize function with a bunch of anonymous functions within and events but it does not make for very easy code to read, extend and maintain.
i tend to use methods like this.setupScene() and this.attachEvents() to abstract different actions. within these methods, i tend to loop through items and call smaller pseudo-private methods that do the singular dom manipulation required or element.addEvent, aka, this.elements.each(function(el) { this._attachEvent(el, "something"); }, this);
If you are sharing code with other users, there are certain conventions most mootools authors try to adhere to when releasing plugins or contributing to the core and more forks.
for example, if the class relates to an element or an array of elements, always set this.element = document.id(el); etc.
for reference on best practices: http://ryanflorence.com/11-tips-for-creating-great-mootools-plugins/
or even http://davidwalsh.name/mootools-class-tips
after a while, they start making perfect sense and will help you tremendously in your work.
There is no benefit whatsoever in using a setup/build/whatever function that is only called from the initialize function of a mootools Class.
I guess the distinction comes from languages where you don't have variable function arguments but rather overload function names with different sets of arguments. Like Java.
In Java this makes perfect sense. When you have multiple constructors that differ in the arguments they accept, then you handle the argument specific operations in your constructor and the common stuff, that every constructor needs to call in a method that is called by all constructors.
Personally I don't make this distinction in my mootools Classes, but rather only outsource functionality in its own function, if there is need to reuse the code from another function.
Don't get confused by setup functions, there is no hidden power to them.
setup is not a private MooTools method. It's not going to do anything until you create an actual method named this way.
There's one instance of this method in MooTools-more, and it's nothing beyond a simple keyword, just like createDefaults() or setInitialStuff() would be.
Keep in mind that MooTools classes are all about extending and reusing existing code. When you extend a class, you might want to change the initialization, but not the setup code. Of course you can run the parent initializer by using this.parent(), but that might introduce unwanted side-effects.
Take this (very simple) example (view live):
var Counter = new Class({
initialize: function(i){
this.i = i;
this.setup();
},
setup: function(){
new Element('p', {
'text': this.i
}).inject(document.body);
}
});
var MultiplyCounter = new Class({
Extends: Counter,
initialize: function(i){
this.i = i * 2;
this.setup();
}
});
new Counter(5);
new MultiplyCounter(5);
The extended class only changes the initialization of i, not the whole implementation of the class. For more complex classes, this creates faster (initialization is only run once) and cleaner (divide and conquer) code.