Re: [mynajs-general] global scoping - Possible Bug

2 views
Skip to first unread message

Tony Zakula

unread,
Oct 13, 2010, 9:42:25 AM10/13/10
to mynajs-...@googlegroups.com
Okay it works now.  However, when you request the link host.com/myna/administrator/ you get an error that states

TypeError: Cannot call method "handleRequest" of undefined

However, on any other URL it works fine.

Comment out the handle request line, and the administrator works fine.

It is not a show stopper, but thought I would report it. 

onApplicationStart:function(){ // run if application cache has expired
 
 $application.set("appEngine",Myna.include($application.directory+"appEngine/appEngine.sjs",{}));
 
 $application.set("appLog",Myna.include($application.directory+"appEngine/appLogging.sjs",{}));
 },
onRequestStart:function(){ // run directly before requested file
appEngine = $application.get("appEngine");
log = $application.get("appLog");
$req.handled = appEngine.handleRequest();
},

On Tue, Oct 12, 2010 at 7:52 PM, Tony Zakula <tonyz...@gmail.com> wrote:
Ha,  Thanks.  I will get the hang of JavaScript pretty soon I promise!  :-)

I am using Myna.log inside of my logging function.  I basically wanted to wrap some functions around Myna logging to make it a little more like apache logger.  

For example, you can use if(log.isInfoEnabled()) log.log("myMessage");

Then inside of log.log, I am using Myna.Log.  

I have info, debug, none right now.  If you wrap your logging with those statements, then you can control the logging level app wide with a setting.  Just seems more efficient and makes for easier trouble shooting down the road.

Tony Z 



On Tue, Oct 12, 2010 at 7:29 PM, Mark Porter <ma...@porterpeople.com> wrote:

The problem is using "var" inside of onRequestStart. That binds to the function scope. You want to either leave it un-var'd or explicitly bind it to the global scope: $server.globalScope.log = $application...

On an unrelated note, why are you not using Myna.log() for logging?

On Oct 12, 2010 3:26 PM, "Tony Zakula" <tonyz...@gmail.com> wrote:
> If you have the following in application.sjs
>
> onApplicationStart:function(){ // run if application cache has expired
>
> $application.set("appEngine",Myna.include($application.directory+"appEngine/appEngine.sjs",{}));
>
> $application.set("appLog",Myna.include($application.directory+"appEngine/appLogging.sjs",{}));
> },
> onRequestStart:function(){ // run directly before requested file
> var appEngine = $application.get("appEngine");
> var log = $application.get("appLog");
> $req.handled = appEngine.handleRequest();
> },
>
> Should you be able to call log.mymethod() from inside an appEngine function?
> I keep getting an undefined error.
>
> Thanks,
>
> Tony
>
> --
> You received this message because you are subscribed to the Google Groups "MynaJS-General" group.
> To post to this group, send email to mynajs-...@googlegroups.com.
> To unsubscribe from this group, send email to mynajs-genera...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/mynajs-general?hl=en.
>

--
You received this message because you are subscribed to the Google Groups "MynaJS-General" group.
To post to this group, send email to mynajs-...@googlegroups.com.
To unsubscribe from this group, send email to mynajs-genera...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/mynajs-general?hl=en.


Mark Porter

unread,
Oct 13, 2010, 12:36:39 PM10/13/10
to mynajs-...@googlegroups.com
It looks like you are modifying the application.sjs file in the root of your Myna instance. Named application settings should normally be limited to a sub-directory,where each sub directory represents a separate application. If you intend to treat the entire Myna instance as a single application, AND you want to maintain the built-in Myna applications, you need to whitelist the myna directory like so:

onRequestStart:function(){ // run directly before requested file
    if (/\/myna\//.test($server.requestDir)){

        appEngine = $application.get("appEngine");
        log = $application.get("appLog");
        $req.handled = appEngine.handleRequest();
    }
},
Or, you can store your appEngine in $server scope ($server.set/get()) if you really want it to handle all applications, regardless of appname.

What is happening is that the "myna_admin" app name is overwriting your global appname, so that $application.get() is checking in the wrong application scope.

In general you should not have a named application.sjs in your root, nor should you nest named applications. 


----------------------------------------------------------
Mark Porter

Myna JavaScript Application Server
Easy web development with server-side JavaScript
http://www.mynajs.org

Tony Zakula

unread,
Oct 13, 2010, 12:55:43 PM10/13/10
to mynajs-...@googlegroups.com
Okay, it did not seem to work like this under the old style.  I had an application where I checked if the URL requested was in the myna directory indicating a built in app, and then let it pass.  So, if you are designing an ecommerce application, how would you ensure that all requests come into your application except for maybe the myna apps?  If my app is in the /myapp directory, I do not want every request coming to my store to have to be prefixed with "/myapp"

Am I missing something again?

Sorry to be a pain, just trying to flesh out these issues.

Thanks,

Tony

Tony Zakula

unread,
Oct 13, 2010, 1:27:49 PM10/13/10
to mynajs-...@googlegroups.com
I guess you could use a web server with rewrite rules, or maybe have a default application?  I know it doesn't matter to internal apps, but URLs are increasingly important to SEO.

Tony Zakula

unread,
Oct 13, 2010, 1:35:19 PM10/13/10
to mynajs-...@googlegroups.com
Another option would be to use a deployer like Java servers.  You could then decide which app answers at the root, and deploy other apps to root/myapp

Mark Porter

unread,
Oct 13, 2010, 2:58:47 PM10/13/10
to mynajs-...@googlegroups.com
"Okay, it did not seem to work like this under the old style"

That was probably because you were not using the $application.set/get() storage which is specific to the currently named application.

You can absolutely do what you want, you just have to  make sure you only handle the requests you want to handle. Here is probably what you want in your global application.sjs

//global application.sjs
{
    onRequestStart:function(){
        //by using server scope, we guarantee that the appEngine is always available
        appEngine = $server.get("appEngine");
        if (!appEngine){
            //since this is global, thread contention is likely            
            Myna.lock("global_app_engine_load",0,function(){
                appEngine = $server.get("appEngine");
                if (!appEngine){
                    appEngine = Myna.include("/myApp/appEngine.sjs",{});
                    $server.set("appEngine",appEngine);
                }
            })    
        }
        //handleRequest should check to see if the URL is a /myna/ url and return 
        //false so that it is handled normally
        $req.handled =appEngine.handleRequest();
    }
}

----------------------------------------------------------
Mark Porter

Myna JavaScript Application Server
Easy web development with server-side JavaScript
http://www.mynajs.org


Tony Zakula

unread,
Oct 13, 2010, 3:22:40 PM10/13/10
to mynajs-...@googlegroups.com
Okay, so one more explanation:

>>That was probably because you were not using the $application.set/get() storage which is specific to the currently named application.
Should I not use the application storage?  Is it better to just include the files in the onRequest?  What is the side affects of thread contention?  Any pros or cons to one way or the other?

Once I work this out, hopefully I will not have to touch it for a long time.

Mark Porter

unread,
Oct 13, 2010, 4:24:58 PM10/13/10
to mynajs-...@googlegroups.com
Well, since you want to handle every requests that comes in, that is really a "server" operation rather than an application operation, so $server is the appropriate scope.

Actually I would recommend just including the files in the onRequestStart. Myna will cache the script , so there should not be any I/O involved, and since parsing is very fast, it adds a negligible amount of time to each request. It also allows each request to have it's own copy so there are no threading issues. Unless you intend to maintain state inside of the object, then there is no need to make it a singleton.

----------------------------------------------------------
Mark Porter

Myna JavaScript Application Server
Easy web development with server-side JavaScript
http://www.mynajs.org


Tony Zakula

unread,
Oct 13, 2010, 4:36:54 PM10/13/10
to mynajs-...@googlegroups.com
So this means keep what we discussed.

Well, since you want to handle every requests that comes in, that is really a "server" operation rather than an application operation, so $server is the appropriate scope.

Below means scrap what we discussed and go back to an include which when you do not use var, will be global for the request. 

Actually I would recommend just including the files in the onRequestStart. Myna will cache the script , so there should not be any I/O involved, and since parsing is very fast, it adds a negligible amount of time to each request. It also allows each request to have it's own copy so there are no threading issues. Unless you intend to maintain state inside of the object, then there is no need to make it a singleton.


Last question, application settings?  Logging Level, URL pattern, etc.  My choices are, a file that gets included with each request. Load out of a database the stored in an included file.  Use option one above so that the initialization happens only once?  Could be hundreds of setting eventually that would be the same for each user.  Or is there an option with Myna.cache to load from file or database and then cache them for long periods of time or until they change.

Thank you for your patience.  Just do not want to rewrite later.

Mark Porter

unread,
Oct 14, 2010, 11:08:48 AM10/14/10
to mynajs-...@googlegroups.com
You got it. The real issue is "will appEngine need to store state, or perform any sort of complex initialization"?. If so it is worth creating a singleton. If it is just loading a function library, includes are just as fast

Application settings:

For settings that are only set during deployment, I like a local JSON file, possible with a "first-run" GUI to set them. Or more commonly, I just set these as local properties on $application, e.g $application.ds="my_app". For anything that a user might have to set after deployment I prefer a database and loading settings at point of use. I always save caching as a last resort because it takes a bit of extra code and it can introduce it's own errors, especially once you start clustering.

To Cache or Not To Cache

I would in general NOT recommend using caching or singleton instances (which is just another form of caching) until an actual performance problem has been identified. If you use constructor objects, you can turn them into singletons later by altering the constructor function like so:

//appEngine.sjs
var appEngine = function(){

}

appEngine.prototype.handleRequest = function(){
...
}

//application.sjs
{
    onRequestStart:function(){ // run directly before requested file
        if (!myApp) myApp={}//create global namespace for myApp if not already created
        Myna.includeOnce("/myapp/appEngine.sjs",myApp);//loads appEngine class Globally into myApp
        
        // create a local instance anywhere we need access to appEngine
        // because he have defined this class inthe global myApp object
        // files inthe subdirectories don't have to use the above includes,
        // just this line below:
        var e = new myApp.AppEngine();
        
        $req.handled = e.handleRequest();
    }
}

Then later you decide you need to make sure everyone is using the same instance of appEngine, you can just modify it's constructor like so:


//appEngine.sjs
var appEngine = function(){
    //see if there is a singleton defined
    var o = $server.get("appEngine");
    if (o) return o;
    
    ... perform any initializations here...
}

The new constructor checks if an appEngine instance is defined in the $server scope and returns that instead of constructing a new instance. That way every call to "new myApp.appEngine()" actually returns the singleton stored in the server scope.

Here is a thread-safe version of the constructor:

//appEngine.sjs
var appEngine = function(){
    //see if there is a singleton defined
    var o = $server.get("appEngine");
    if (o) {
        return o;
    } else {
        Myna.lock("load_app_engine",10,function(){
            //see if there is a singleton defined by another thread
            // before we got our lock    
            o = $server.get("appEngine");
            if (!o){
                $server.set("appEngine",this);
                o=this;
                
                ... perform any initializations here...
            }
        })    
    }
    return o;
}


Since it seems I write code like that fairly often, I'm adding a new function Myna.threadSafeSet(scope, prop, func) that will simplify the above code to :

//appEngine.sjs
var appEngine = function(){
    return threadSafeSet($server,"appEngine",function(){
        .. perform any initializations here...
        return this;
    })
}


This will be available in the next Myna release

----------------------------------------------------------
Mark Porter

Myna JavaScript Application Server
Easy web development with server-side JavaScript
http://www.mynajs.org


Tony Zakula

unread,
Oct 15, 2010, 3:59:05 PM10/15/10
to mynajs-...@googlegroups.com
Okay.  Thank you for hanging with me.  Now I know the in and outs of the whole server and scope genre.  At this point, I am opting for select includes.  It seems simpler and less error prone.  The project can m ove along now.  :-)

Tony Z
Reply all
Reply to author
Forward
0 new messages