dev@cloudburo

Meteor Small Hints #1: Execute a function by name which works on client and server

Calling a function by name in Meteor gave me at the beginning some headache concerning the passed context.
I.e. the helper function which manages the function by name execution, must work on the client, as well as server side, due to the fact that a Meteor method may be configured for both sides.

In my code I have a configuration object which has as an attribute initfunc, which defines a function which should be called in an initalization process.


  {  
       name: 'SiteName'  
       type: 'singleton' 
       singletonSpace: 'sitename'  
       initfunc: 'Extension.getSiteName' 
   } 


The initfunc will be used in the following method call, e.g.


  getDefaultFeatureValue: (name) -> 
     configFeature = this.getConfigFeature name 
     if configFeature.initfunc != undefined 
          Plattform.executeFunctionByName configFeature.initfunc window 
     else 
            configFeature.init 


As one can see I pass to the executeFunctionByName the function name, as well as the global window variable as context. The execute function is looking like that.


  executeFunctionByName : (functionName, context) -> 
       args = Array::slice.call(arguments, 2) 
       namespaces = functionName.split('.') 
       func = namespaces.pop() 
       i = 0 
       while i < namespaces.length 
              context = context[namespaces[i]] 
               i++ 
       value = context[func].apply context, args 
       return value 


Now this worked fine to the point where getDefaultFeatureValue was called from a server method as well.

The problem was that the global variable window is only defined on the browser. The variable isn’t available in the NodeJS server part and you have to use the global variable.


  Plattform.executeFunctionByName configFeature.initfunc global 


I extended the code, so that the caller may not provide the context (in case he wants to call the function in a global context). The function itself will look then for a global context (the typeof code part in the beginning)


      executeFunctionByName : (functionName, context) -> 
        if typeof context == 'undefined' 
          if  typeof global == 'undefined' 
            context = window 
          else 
            context = global 
        args = Array::slice.call(arguments, 2) 
        namespaces = functionName.split('.') 
        func = namespaces.pop() 
        i = 0 
        while i < namespaces.length 
          context = context[namespaces[i]] 
          i++ 
        value = context[func].apply context, args 
        return value 


The final method was simplified not having to provide the global context.


  getDefaultFeatureValue: (name) -> 
      configFeature = this.getConfigFeature name 
      if configFeature.initfunc != undefined 
        Plattform.executeFunctionByName configFeature.initfunc 
      else 
        configFeature.init 

comments powered by Disqus