Why is global object becoming undefined?

25 views
Skip to first unread message

Eric Dykstra

unread,
Aug 7, 2013, 12:47:44 PM8/7/13
to sil...@googlegroups.com
Hey everyone,

I have a strange issue I am so far unable to debug.

I have a code set that is the exact same on my development and staging servers. (git pull)

The page loads perfectly on the development server, but I get a 'Template Error'
on the staging server.

Initially, the Template Error was simply;

Res.STOP()

I started simplifying the page, by replacing 'include' statements (for HTML partials) with the actual code.

I then found the error is:

TypeError: Cannot call method 'getPageDataByID' of undefined.

That line contains:

<%= global.CMS.getPageDataByID(1001002) %>

Which means that for some reason, global.CMS is not defined.

To confirm this is the issue, I replaced the above with hard-coded text, and the staging server then loads the page fine.

I do define global.CMS in the server.js file, being executed by the SilkJS server, with debug statements:

.....

global.CMS = require('apps/clubs/templates/assets/cms_api/CMS.sjs');

console.log('global.CMS =');
console.dir(global.CMS);

.....

Note that on both the development and staging servers, this outputs:

global.CMS =
/var/www/acs/server.js line 38:
 (object) :
 [getPageDataByID] : function() { ... }

For reference, the CMS.sjs file contains:


exports = {
    
    getPageDataByID:             function(data_id){

        .....

    }
};

So, the question I guess is ......

Why is the definition of global.CMS lost once it is time to load the page on the staging server?

Anyone have any idea what this bug is, or how to debug further?

Thanks!

Eric


Michael Schwartz

unread,
Aug 7, 2013, 12:51:47 PM8/7/13
to sil...@googlegroups.com, sil...@googlegroups.com
My bet would be some code is storing to global or global.CMS sometime after your console.dir call.



Sent from my iPad
--
You received this message because you are subscribed to the Google Groups "SilkJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to silkjs+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Eric Dykstra

unread,
Aug 7, 2013, 1:00:36 PM8/7/13
to sil...@googlegroups.com
Mike,

Nothing is being stored to global.CMS after the console.dir call, but there are places where I store to global.

Please excuse my ignorance, but why is that wrong to do?

In fact, I have this code segment which stores to global many times.

// ----------------------------------
// Define Global objects:
// ----------------------------------

global.Addresses     = require('rpc/Addresses.sjs');
global.Messages      = require('rpc/Messages.sjs');
global.Persons       = require('rpc/Persons.sjs');
global.Volunteers    = require('rpc/Volunteers.sjs');

global.Clubs         = require('rpc/Clubs.sjs');

global.CMS           = require('apps/clubs/templates/assets/cms_api/CMS.sjs');

console.log('global.CMS =');
console.dir(global.CMS);          // Here is the call to console.dir you mention. As you can see below, I store to global again immediately.

// ----------------------------------
// Initialize Global variables:
// ----------------------------------

global.ACS_CMSBrowseMode = false;
global.ACS_CMSEditMode   = false;


What is wrong with the above?

Thanks,

Eric

Michael Schwartz

unread,
Aug 7, 2013, 1:15:02 PM8/7/13
to sil...@googlegroups.com, sil...@googlegroups.com
Nothing in the code you show. In a short jst,can you console.dir CMS?

What I meant was some code you're executing is doing global.CMS = somethig_wrong

global['CMS'] =...

Or

global = ...

Are any of your global.* variables accessible?




Sent from my iPad

Michael Schwartz

unread,
Aug 7, 2013, 1:28:22 PM8/7/13
to sil...@googlegroups.com, sil...@googlegroups.com
This line looks like an assignment:


console.log('global.CMS =');


Sent from my iPad

On Aug 7, 2013, at 10:00 AM, Eric Dykstra <virtu...@gmail.com> wrote:

> console.log('global.CMS =');

Eric Dykstra

unread,
Aug 7, 2013, 1:38:17 PM8/7/13
to sil...@googlegroups.com
Mike,

It is inside quotes - so I figured it would not be an issue. However, just to be on the safe side, I changed those debug statements to use 'globe' instead of 'global'.

When I start the server, I get this dumped out:

----------------------------------
From server.js file.
----------------------------------
 
globe.CMS =
/var/www/acs/server.js line 51:
 (object) :
 [getPageDataByID] : function() { ... }

CMS =
/var/www/acs/server.js line 54:
 (object) :
 [getPageDataByID] : function() { ... }

globe.ACS_CMSBrowseMode =
/var/www/acs/server.js line 57:
 (boolean) false

Then when I load the test page, I get the following which was placed immediately after the <html ...> tag:

----------------------------------
From very top of test page.
----------------------------------
globe.CMS =
undefined line 12 (eval):
 (object) :
 [getPageDataByID] : function() { ... }

CMS =
undefined line 15 (eval):
 (object) :
 [getPageDataByID] : function() { ... }

globe.ACS_CMSBrowseMode =
undefined line 18 (eval):
 (boolean) false


This is all on my development server which still loads the page properly, even though the global.CMS is undefined.

So, the error is happening on both servers, but for some reason is not an issue (yet) on the development server.

Note that I also tried dumping the CMS object and other global variables per your request. As you can see, they are also undefined when the page is loaded.

Thoughts?

This REALLY has me stumped.

Thanks,

Eric

Michael Schwartz

unread,
Aug 7, 2013, 1:49:07 PM8/7/13
to sil...@googlegroups.com
I'm confused.

The prints from the very top of test page are showing valid objects.

The fact that you have different behaviors in the two environments is a clue.  Something data driven perhaps?  (Different value in the database causes different path of execution in your code)


Eric Dykstra

unread,
Aug 7, 2013, 1:57:30 PM8/7/13
to sil...@googlegroups.com
Mike,

I'm sorry - you are right. The output is the same - at least for the object. I saw the undefined for the line number and jumped to conclusions.

So, I figured my assumption that the error was on the development server also was wrong, and I deployed to the staging server.

Now I get the same results there and it works! The page is loaded and served fine!

This makes no sense, since I have done nothing to the database, and the only thing I changed on the source code was the console.log/dir statements.

Maybe it was the 'global.CMS=' in quotes? Going to put that back to the way it was and see. Will update soon.

Eric

Eric Dykstra

unread,
Aug 7, 2013, 2:03:38 PM8/7/13
to sil...@googlegroups.com
Mike,

It was not the 'global.CMS = ' in the console.log statement.

That makes sense because it is in quotes, and the statement wasn't even there until I tried to debug the issue.

I am completely baffled as to what just transpired. All I know is that it cost me a few hours. Ugh.

I wonder if what you said is still true though - maybe there is a 'global.CMS = something_wrong' somewhere in the code and it is overwriting memory in such a way that the bug is intermittent? Is that even possible?

Thanks,

Eric

Michael Schwartz

unread,
Aug 7, 2013, 2:14:07 PM8/7/13
to sil...@googlegroups.com
If you don't mind, I can make a few suggestions about your coding practice for SilkJS?

You can do:

include.path.push('rpc');

Then you can:
global.Addresses = require('Addresses.sjs')

You can include.path.push('apps/clubs/tempaltes/assets/cms_api'), too

You generally don't use .extensions on names in require.

So require('foo.js') works, but isn't best practice.  You'd want to do require('foo') - require() will do the right thing.

The .sjs extension is intended for server-side JavaScript code, which SilkJS handles specially.  It is meant to be used like .php scripts with Apache.  That is, the server sees /index.sjs, finds index.sjs in your document root, and executes it.

The require() functionality is really neat.  You can require() a directory as well as a file.  The directory must have an index.js file inside, which is actually loaded.  For example:

cms/
cms/index.js:
   exports.extend({
     CMS_main: require('lib/main'),
     CMS_extras: require('lib/extras'),
     … // etc.
   });
cms/lib
cms/lib/main.js:
   exports = function() { 
     // whatever your CMS_main() does
   };
cms/lib/extras.js
   exports = function() {
     // whatever your CMS_extras() does
   };

Then in your code, you can:

var main = require('CMS').CMS_main,
      extras = require('CMS').CMS_extras;

See?

Your use of global namespace is fine, but some people will find you're "polluting" the global namespace.

One of the beautiful things about require() is that you can use it many many times on the same module.  The first time, it compiles the module and returns its exports variable/object.  The second, third, etc., time, it just returns the exports variable/object again - no compilation is done the 2nd, 3rd, etc. times.

So, in your .jst files, you can:

<%
   var CMS = require('CMS');

   // use CMS
%>

You can require('CMS") in all of your .jst with a teeny tiny performance penalty (the time to lookup the filename 'CMS' in a hash and return its exports object .  So small I wouldn't worry about it (code correctness and readability is better).

This scheme lets you "pollute" just your .jst program's space, without messing with global.

Call it the force.  Use the force, luke!

Otherwise, your code has a very nice style to it.

Eric Dykstra

unread,
Aug 7, 2013, 2:18:06 PM8/7/13
to sil...@googlegroups.com
Mike,

Of course I don't mind. In fact I appreciate it!

I will make the coding style changes you suggest.

Thanks,

Eric

Eric Dykstra

unread,
Aug 7, 2013, 2:33:27 PM8/7/13
to sil...@googlegroups.com
Mike,

I am implementing your coding style suggestions, once step at a time ....

This works:

require.path.push('apps/clubs/templates/assets/cms_api');

global.CMS           = require('CMS.sjs');

This fails:

require.path.push('apps/clubs/templates/assets/cms_api');

global.CMS           = require('CMS');

with the error:

*** Could not compile /var/www/acs/server.js
Could not locate require file CMS

Why doesn't require 'do the right thing'?

Eric

Michael Schwartz

unread,
Aug 7, 2013, 2:40:16 PM8/7/13
to sil...@googlegroups.com
The require() function searches the require.path array for the files you ask it to load.

require() does not know about .sjs extensions.  You'll have to rename the files to .js (which is best practice)

Make sense?

The include() function also has include.path array, FWIW.  If you include() in a .jst file, it uses include.path.


This allows you to have your main .jst executables in your documentRoot and the snippets you include() can be outside your documentRoot.  Outside means some user won't be able to fiddle with the address in his browser and fetch the snippet.



Eric Dykstra

unread,
Aug 7, 2013, 2:54:17 PM8/7/13
to sil...@googlegroups.com
Mike,

I thought that the beauty of *.sjs was that they were compiled automatically if changed, where as a change to a *.js file required a server restart?

I will be moving the include files outside the 'traditional' docroot just in case, but since I am dynamically changing docroot using HttpChild.requestHandler, they are not reachable by fiddling with the address bar.

Eric 

Michael Schwartz

unread,
Aug 7, 2013, 3:00:54 PM8/7/13
to sil...@googlegroups.com
That is the beauty of .sjs

In documentRoot make a file, foo.sjs:

res.write('hello, world')

Run your server


Edit foo.sjs:

res.write('goodbye cruel world')

reload the browser (do not restart the server)

I would almost make all my Ajax services .sjs on the server side, FWIW.  Using .jst for your HTML needs and .sjs for your services needs, you would only need to restart silkjs server to change your schemas or otherwise load any require() code you change.

You realize you can use .jst for css, too?

<% var color = '#ff00ff'; %>

.purple_div {
   background-color: <%= color %>;
}

(you can make .rss, .xml, etc. - whatever floats your boat!)



The require() method follows the commonJS specification.  It's not supposed to recompile a require'd module, ever.

Eric Dykstra

unread,
Aug 7, 2013, 3:27:12 PM8/7/13
to sil...@googlegroups.com
Mike,

2 things confused me ....

First, I agree with this; "I would almost make all my Ajax services .sjs on the server side, FWIW."

But, you had said; 

"You can do:

include.path.push('rpc');

Then you can:

global.Addresses = require('Addresses.sjs')

You generally don't use .extensions on names in require."

Since it is a Ajax service (See the /rpc reference) I want it to be sjs.

Second, I had not idea about this; "The require() method follows the commonJS specification.  It's not supposed to recompile a require'd module, ever."

So, of course you want to make sure you follow the spec - but without knowing about the spec, I assumed require() would find the *.sjs too.

Lots to learn - and as always - your feedback is very valuable.

Thanks,

Eric

Michael Schwartz

unread,
Aug 7, 2013, 4:12:53 PM8/7/13
to sil...@googlegroups.com, sil...@googlegroups.com
To make services .sjs, just put the service.sjs files in documentRoot.  The contents are plain old JavaScript, as if in a function called by the server.  They have full access to your global variables, including req and res.

You can have /foo.sjs, /foo/bar.sjs, etc.

My preference is to make my services as .sjs files and have them require() common code.

So three layers, basically:
1. Service .sjs
2. API
3. ORM

You might have a document API that needs to access a Document, its creator (User), its editor (User), Permissions, etc.  That method would call the ORM to gather the related information.

I suppose I could have implemented a require.extensions array so you could tell it to look for non-standard extensions like .sjs.  If you need it, I'll implement it.  But you probably won't benefit from it as much as using .sjs the way it's designed?  ;)
Sent from my iPad

Eric Dykstra

unread,
Aug 11, 2013, 1:10:14 PM8/11/13
to sil...@googlegroups.com
Mike,

Putting this here for anyone else that is digging for this tidbit.

Maybe I missed it in the docs, but when adding JST to the CSS file, obviously the file needs to be renamed to style.css.jst, or just style.jst and the include in the HTML needs to have that new name too.

But, that wasn't enough. 

When you commented that I needed to add the following to the top of the CSS file - everything worked perfect.

<% res.contentType = 'text/css'; %>

Some of the CSS references images, and those are DB driven, so I needed to be able to embed JST inside the CSS file. This works perfect.

Thanks again for the awesome support.

Eric

On Wednesday, 7 August 2013 15:00:54 UTC-4, mschwartz wrote:

Michael Schwartz

unread,
Aug 11, 2013, 1:57:55 PM8/11/13
to sil...@googlegroups.com
The best documentation for this is both the httpd source code, and on the silkjs.net www site:


I quote:

The res object represents information your code is preparing to send to the client as the response. This is an example dump of the res object:

 [contentType] : (string) text/html

You may set the response code to something other than 200 (OK) by setting res.status. You may set the content's MIME type to something other than text/html by setting res.contentType.

/quote

JST scripts are effectively just subroutines that are created by compiling JST->JavaScript and creating  something like a Function constructed with the compiled code as the body.  When the appropriate URI is requested, the subroutine is called.

Thus they have access to global.everything, including req, res, etc.

(The same is true of .sjs, but there's no compilation, the source is pure JS)

Granted the documentation could be better if there weren't some docs in emails like this, some on the github wikis, and some on silkjs.net.
Reply all
Reply to author
Forward
0 new messages