CoffeeScript namespace exports in Meteor custom packages - coffeescript

For a few hours, I couldn't get my Meteor package to export variables defined in CoffeeScript files.
In foo.coffee, for example, I tried using #Foo = {foo: 1}, Foo = {foo: 1}, exports.Foo = {foo: 1}, and so on and so forth, but nothing would work.
Finally, after looking at the Meteor coffeescript test package on github, I placed the api.export(); call before the api.on_use() call for that package and it worked.
Any idea why?
My package is set up as follows:
Directory
foo/
.meteor/
.build/
foo.coffee
package.js
foo.coffee
class FooBar
constructor: ->
Foo =
FooBar: FooBar
package.js
Package.describe({
summary: "A package that makes foo with foobar"
});
Package.on_use(function(api) {
api.use("coffeescript", "client");
api.export("Foo", "client"); // <-- Moved this to *before* the on_use declaration
api.add_files("foo.coffee", "client");
});

Coffeescript compiles
#Foo =
FooBar: FooBar
to
(function() {
this.Foo = {
FooBar: FooBar
};
}).call(this);
You need to remove the this. before Foo, take a look at namespace, but it's not a good idea since you might need to compile it once you modify the original coffee files.
Here's my trick:
Add a file, named global_variables.js:
Foo = this.Foo;
Then add it in your package.js:
api.add_files('xxx', 'xxx', 'global_variables.js');
Then it just works!

Another plain & simple solution:
lib/namespace.coffee
Users = {} # This is a local variable
#Users = Users # Globalize your variable
package.js
api.addFiles('lib/namespace.coffee');
api.export('Users');

Related

How should I document singletons?

I'm not sure which tag I should use to document singletons like the following:
var singleton = {
c: 1,
f: function(){
return this.c;
}
}
When I use #namespace the member function f will be declared as <static>, and I'ts not a namespace anyway. But it's obviously neither a #class. Is there a separate tag or trick you use for singletons?

Coffeescript class shares properties

I've detected a very weird behavior in coffeescript.
class Foo
list: []
add: (val)->
#list.push(val)
x = new Foo()
x.add(1)
console.log(x.list.length) // 1
y = new Foo()
y.add(1)
console.log(y.list.length) // 2
So as you see the #list property got shared between the two class instances in a strange way.
I've never faced similar issue before, in coffeescript.
Convert it to JavaScript:
var Foo, x, y;
Foo = (function() {
function Foo() {}
Foo.prototype.list = [];
Foo.prototype.add = function(val) {
return this.list.push(val);
};
return Foo;
})();
As you can see, Foo.prototype.list is a property of the prototype, not of an instance of your class. There's only one array and it will be shared across all of the instances of your class.
To make list an instance variable, add it to the constructor:
class Foo
constructor: ->
#list = []
add: (val)->
#list.push(val)

#function in CoffeeScript does not work as advertised

Rails 3.2.8. In converting some of my JS functions over to CoffeeScript, I have come across several questions that say declaring a function like so:
#foo = (bar) ->
puts foo in the global namespace. But it doesn't, because my function calls elsewhere in the application, especially ones that are in .js.erb files.
Here's what does work:
foo = (bar) ->
window.foo = foo
With that, all my calls in .js.erb files work fine.
What is the reason that the #foo = notation doesn't work as I am expecting it to? That would be a lot easier than having to remember to add an extra line to expose the function to the global namespace.
#foo translates to this.foo
foo translates to var foo
There is a big difference between the two of those.
For example:
bar = (baz) ->
#bar = 5
lemon = #bar + baz
#foo = (bar) ->
lemon
return #
Bar = new bar(12)
Translates to:
var Bar, bar;
bar = function(baz) {
var lemon;
this.bar = 5;
lemon = this.bar + baz;
this.foo = function(bar) {
return lemon;
};
return this;
};
Bar = new bar(12);
See fiddle demo of generated code: http://jsfiddle.net/maniator/rXWw2/
Here is a link showing you the CoffeeScript and it's generated code
I just wanted to add something that seems important to understanding why the initial
#foo = () -> "abc"
doesn't add to the global window object.
Coffeescript wraps - once compiled, it wraps all contents of every .coffee-file into a surrounding anonymous function that is immediately executed.
Thusly, and explicitely for that reason, the global namespace is not polluted, thereby implicitely protecting the dev from creating "evil" globals.
So, your foo becomes a member function of an anonymous wrapper function - how seriously useless gg ...
I guess what you want anyway is your global config object or something, to which you simply add your definitions --- you surely didn't mean to really create globals just for quick and easy access now, did you?? :)

CoffeeScript: why won't my class work without prefixing with window?

I have an external file where I have defined my class:
class MyClass
constructor: ->
alert 'hello'
When the CoffeeScript is compiled into JavaScript, it wraps it with a closure. So when I try to use it in some JavaScript:
$(function(){
var ob = new MyClass();
});
I get the error:
Uncaught ReferenceError: MyClass is not defined
but if I prefix the class name with window, it will work:
class window.MyClass
constructor: ->
alert 'hello'
How can I define my class without prefixing with window?
You can compile the CoffeeScript with the --bare but that's generally not recommended because you may pollute the global namespace.
My suggestion is to attach the class to the window object, like your second example, or even better, use this namespace function from the CoffeeScript docs to attach your classes to a single global object attached to the window
namespace = (target, name, block) ->
[target, name, block] = [(if typeof exports isnt 'undefined' then exports else window), arguments...] if arguments.length < 3
top = target
target = target[item] or= {} for item in name.split '.'
block target, top
# Usage:
#
namespace 'Hello.World', (exports) ->
# `exports` is where you attach namespace members
exports.hi = -> console.log 'Hi World!'
namespace 'Say.Hello', (exports, top) ->
# `top` is a reference to the main namespace
exports.fn = -> top.Hello.World.hi()
Say.Hello.fn() # prints 'Hi World!'
Source: CoffeeScript FAQ

Namespacing in coffeescript

Is there any intrinsic support for namespacing in coffeescript?
Adequate namespacing seems like something coffeescript could really help with although I don't seem to be able to find anything to suggest that there is support for this.
I prefer using this pattern for "namespacing". It isn't really a namespace but an object tree, but it does the job:
Somewhere in the startup of the app, you define the namespaces globally (replace window with exports or global based on your environment.
window.App =
Models: {}
Collections: {}
Views: {}
Then, when you want to declare classes, you can do so:
class App.Models.MyModel
# The class is namespaced in App.Models
And when you want to reference it:
myModel = new App.Models.MyModel()
If you don't like the global way of defining namespaces, you can do so before your class:
window.App.Models ?= {} # Create the "namespace" if Models does not already exist.
class App.Models.MyModel
A way to make it simple to reference the class both in it's own "namespace" (the closed function) and the global namespace is to assign it immediately. Example:
# Define namespace unless it already exists
window.Test or= {}
# Create a class in the namespace and locally
window.Test.MyClass = class MyClass
constructor: (#a) ->
# Alerts 3
alert new Test.MyClass(1).a + new MyClass(2).a
As you see, now you can refer to it as MyClass within the file, but if you need it outside it's available as Test.MyClass. If you only want it in the Test namespace you can simplify it even more:
window.Test or= {}
# Create only in the namespace
class window.Test.MyClass
constructor: (#a) ->
Here's my personal implementation :
https://github.com/MaksJS/Namespace-in-CoffeeScript
How to use in the browser :
namespace Foo:SubPackage1:SubPackage2:
class Bar extends Baz
#[...]
How to use in CommonJS environment :
require './path/to/this/file' # once
namespace Foo:SubPackage1:SubPackage2:
class Bar extends Baz
#[...]
From the section about namespacing on the wiki: https://github.com/jashkenas/coffee-script/wiki/FAQ
# Code:
#
namespace = (target, name, block) ->
[target, name, block] = [(if typeof exports isnt 'undefined' then exports else window), arguments...] if arguments.length < 3
top = target
target = target[item] or= {} for item in name.split '.'
block target, top
# Usage:
#
namespace 'Hello.World', (exports) ->
# `exports` is where you attach namespace members
exports.hi = -> console.log 'Hi World!'
namespace 'Say.Hello', (exports, top) ->
# `top` is a reference to the main namespace
exports.fn = -> top.Hello.World.hi()
Say.Hello.fn() # prints 'Hi World!'
You must really check out CoffeeToaster:
https://github.com/serpentem/coffee-toaster
It comes with a packaging system that when enabled will use your folder's hierarchy as namespaces declarations to your classes if you want so, then you can extends classes from multiple files, do imports and son, such as like:
#<< another/package/myclass
class SomeClass extends another.package.MyClass
The build configuration is extremely minimalist and simple, made to be obvious:
# => SRC FOLDER
toast 'src_folder'
# => VENDORS (optional)
# vendors: ['vendors/x.js', 'vendors/y.js', ... ]
# => OPTIONS (optional, default values listed)
# bare: false
# packaging: true
# expose: ''
# minify: false
# => HTTPFOLDER (optional), RELEASE / DEBUG (required)
httpfolder: 'js'
release: 'www/js/app.js'
debug: 'www/js/app-debug.js'
There's also a debug option that compile files individually for ease the debugging processes and another useful features.
Hope it helps.
Note that it is possible to write:
class MyObject.MyClass
constructor: () ->
initializeStuff()
myfunction: () ->
doStuff()
if you declared an object/ns MyObject.
And while we're at it, here's my implementation of a jquery-ns-function:
(function($) {
$.namespace = function(namespace, initVal) {
var nsParts = namespace.split("."),
nsPart = nsParts.shift(),
parent = window[nsPart] = window[nsPart] || {},
myGlobal = parent;
while(nsPart = nsParts.shift()) {
parent = parent[nsPart] = parent[nsPart] || {};
}
return myGlobal;
}
})(jQuery);
I Strongly suggest using requirejs.org or similar battle tested module loaders.
Especially if you want to load stuff asynchronously.
Rolling your own namespacing/module scheme is really hard if you disregard
the simply, easy and naive approaches
As I'm also busy to learn the best way of structuring the files and use coffeescript in combination with backbone and cake, I have created a small project on github to keep it as a reference for myself, maybe it will help you too around cake and some basic things. All .js (with cake compiled files) are in www folder so that you can open them in your browser and all source files (except for cake configuration) are in src folder. In this example, all .coffee files are compiled and combined in one output .js file which is then included in html.
Based on some of the answers here on StackOverflow I have created small util.coffee file (in src folder) that exposes "namespaces" to the rest of the code.