23

I'm using Rails 3.1 with CoffeeScript and have run into a snag. How do I call a function from a .js.erb file that is located in a .js.coffee file?

Say the function in .js.coffee is the following:

myName = -> "Bob"

I would think I could just call it like any regular js function such as:

var theName = myName();

but that doesn't seem to work. Any ideas?

or is it possible to use coffeescript in my .js.erb file to make everything the same?

mu is too short
  • 426,620
  • 70
  • 833
  • 800
Brad
  • 231
  • 2
  • 3

1 Answers1

56

The reason you can't call the CoffeeScript function directly, is that CoffeeScript is wrapped in an immediately-invoked function when compiled. This is done to keep your code from polluting the global namespace.

This is generally A Good Idea™, but of course you can get around it when you need to. If you want a function or other variable to be accessible everywhere (i.e. global scope), you can simply say

window.myName = -> "Bob"

That way, the function is added directly to the global scope, and you can call it from anywhere as window.myName() (or simply as myName() unless the function's being shadowed by a local one).

However, to keep the global namespace as clean as possible, it's best to define a namespace for yourself (like jQuery does, by putting everything into the $ object). For instance, in your first CoffeeScript or JavaScript file (i.e. the first file to be loaded), you can do something like this

window.myNamespace = {};

Then, whenever you want something to be available elsewhere, you can add it to that namespace:

window.myNamespace.myName = -> "Bob"

And then you can call it from anywhere, using window.myNamespace.myName() or simply myNamespace.myName().

Alternatively, you can use CoffeeScript's "assign if undefined or null" operator at the top of all your files:

window.myNamespace ?= {} # create myNamespace if it doesn't already exist

Whichever file gets evaluated first will create the missing window.myNamespace object. Subsequent code will just see that it already exists and skip the assignment. Point is, it'll always be available, regardless of evaluation order.

Edit: Made myNamespace lower-camelcase since it's basically a variable; not a constructor/class

Addendum: You can avoid the function wrapper by using the -b/--bare command line switch, but as mentioned the wrapper is a good thing.

Flambino
  • 18,507
  • 2
  • 39
  • 58
  • Awesome. Thanks so much for the great answer. Looking forward to trying it out this evening. – Brad Feb 13 '12 at 15:21
  • Goodness, what a fantastic answer :) – simonmorley Jan 16 '14 at 11:47
  • Shortcut to `window.myName`: just write `@myName` – Fabian Horlacher Dec 31 '14 at 15:30
  • This is a great answer. Good resource for reading more on this? – Jeff May 15 '15 at 20:29
  • 1
    @Jeff Sorry to say that I can't point to any single resource; namespacing code and avoiding global scope pollution with IIFEs are basic JavaScript best practices, and can be found all over the place. CoffeeScript just follows that when compiling. The rest is just JavaScript and CoffeeScript syntax and semantics, and standard browser runtime behavior (e.g. `window` being the global object). So it's all aspects of "general client-side development", but each is a topic own its own. But a book on client-side CoffeeScript (or Rails, since that's where it's often used) would probably have something. – Flambino May 24 '15 at 15:06