3

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
1
  • why don't you use requirejs/amd? Commented Nov 26, 2012 at 23:01

3 Answers 3

2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Okey, I will use RequireJS, but how can I use RequireJS with jasmine-node tests?
Updated with example using it with Jasmine.
I wrote this test pastebin.com/cn9T751j but when I started it, it will gave me result: Finished in 0 seconds 0 tests, 0 assertions, 0 failures
See the link about custom runners. There's also examples in this project. If you still can't figure it out, I would ask a new question. This is no longer about namespaces in CoffeeScript, and instead about how to run jasmine-node to test RequireJS modules.
1

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!'

Comments

0

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

a.coffee

root = exports ? window
MyClasses = root.MyClasses = root.MyClasses ? {}

class MyClasses.Aa
  @someProp: true

b.coffee

# same header as a.coffee
class MyClasses.Bb
  @someProp2: false

c.coffee

# 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.

1 Comment

Is there any way to do that without repeating header or using RequireJS?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.