Reputation: 490
How can I create namespace for my classes written in CoffeeScript?
For example, I have tree classes Aa, Bb and Cc. I want them insert into globaly assesible namespace - MyClasses, allow comunicate cross them and using them in jasmine-node.
class MyClasses.Aa
@someProp: true
class MyClasses.Bb
@someProp2: false
class MyClasses.Cc
@doSomeStuff: -> MyClasses.Aa.someProp = false
I know, I can inject them into one file and compile, but I want to have one class = one file.
How can I do that please? Thank you!
EDIT: I tried this way, but I think it is not good, but it works in browser and jasmine-node
root = exports ? this
root.MyClasses = root.MyClasses ? {}
root.MyClasses.Aa =
class Aa
Upvotes: 3
Views: 2222
Reputation: 1291
There is a suggested solution in the Coffescript wiki:
from 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!'
Upvotes: 1
Reputation: 15276
Use RequireJS.
In one file called "my-classes.coffee", define the namespace.
define [], ->
# You need this if you want it to work in multiple environments.
# Otherwise just use `window` to work in the browser.
root = exports ? this
root.MyClasses = {}
You can define your class in another file called "aa.coffee".
define ['my-classes'], (MyClasses) ->
class MyClasses.Aa
@someProp: true
Another file:
define ['my-classes'], (MyClasses) ->
class MyClasses.Bb
@someProp2: false
Now when you require, it should export MyClasses
which includes MyClasses.Aa
.
require ['my-classes', 'aa'], (MyClasses, _) ->
console.log MyClasses.Aa.someProp
One issue with doing it this way is that you can't depend on just "my-classes" in the require
statement. If you did that, MyClasses.Aa
would be undefined. But you can't depend on just "aa" either, because "aa" doesn't export anything except by adding to MyClasses. In the above code snippet, MyClasses.Bb
is undefined because I haven't explicitly depended on it. This is why many people either use one giant file or duplicate the boilerplate of re-exporting the namespace.
If anyone knows how to fix this, please let me know.
I, personally, find RequireJS to be complicated to use, and there are many different ways to set it up. One way I've used it with jasmine is by using a cake task to precompile my CoffeeScript down to JavaScript, and then have spec files like this.
requirejs = require('requirejs')
# Set the baseURL to your compiled JS dir.
requirejs.config { baseUrl: __dirname + '/../lib' }
requirejs ['my-classes', 'aa'], (MyClasses, _) ->
describe "someProp", ->
it "should be true", ->
expect(MyClasses.Aa.someProp).toEqual true
This may not be the best way, but I was able to use it to run modules in the browser, on a Node server, and in jasmine-node tests. I've also seen some people use custom runners to avoid the boilerplate in their spec files.
If you'd rather not use RequireJS, you may find this question helpful. It works by using the namespace
function defined on the CoffeeScript FAQs.
Upvotes: 2
Reputation: 42031
You could compile your CoffeeScript files with the -b
flag which removes the safety wrapper. Or expose your classes to the global scope with something like you already have
root = exports ? window
MyClasses = root.MyClasses = root.MyClasses ? {}
class MyClasses.Aa
@someProp: true
# same header as a.coffee
class MyClasses.Bb
@someProp2: false
# same header as a.coffee
class MyClasses.Cc
@doSomeStuffWith: (someClass)->
# pass a class to the method instead of just
# modifying another class within the same scope
someClass.someProp = false
Repeat for other classes. In the first line I find window
much more explicit than this
, also the second line allows you to just append classes to MyClasses
without long sequences of namespaces.
In the browser your classes will be inside the global object MyClasses
In node.js you would use them like this (which is a little verbose IMHO):
var Aa = require("./a").MyClasses.Aa,
Bb = require("./b").MyClasses.Bb,
Cc = require("./c").MyClasses.Cc;
console.log(Aa.someProp) // true
console.log(Bb.someProp2) // false
Cc.doSomeStuffWith(Aa)
console.log(Aa.someProp) // false
pd: I haven't checked it with node-jasmine.
Upvotes: 0