Project

General

Profile

Feature #3005

Updated by Gregory Magarshak over 1 year ago

h2. Objective 

 Typically, each Plugin in an App loads its Module file, which defines the various tools etc. that can be loaded on demand. Including: 

 <pre> 
 Users.js 
 Streams.js 
 Websites.js 
 Assets.js 
 Places.js 
 </pre> 

 and the proprietary plugins: 

 <pre> 
 Travel.js 
 Calendars.js 
 Communities.js 
 Media.js 
 </pre> 

 However, the Module files have become bloated with various methods of which 99% are not used in a given app, especially on startup. That code can be loaded later, on demand when needed. 

 We will leave the synchronous methods in these modules as they are. However, almost all the asynchronous methods can be migrated to other files, using the mechanism described below. Asynchronous methods are the ones which return a @Promise@, or have a callback (you can literally search for the word @callback@ in function signatures), or perhaps take functions or @options@ objects with possible names like @onSuccess@, @onCancel@ etc. 

 h2. Mechanism to use 

 The @Q.Method@ class defined in @Q.js@ is designed to help you refactor such asynchronous methods into methods that first may load an some external JS code, and only then call the method. (Obviously, synchronous methods have no time to do that, since they must return the result in the same thread.) 

 The procedure is simple: when you find an object with asynchronous methods in a file named @MyPlugin/js/MyPlugin.js@, like so: 

 <pre> 
 SomeObject = { 
   someMethod: function _someMethod (foo, bar, callback) { 
      someClosureVariable.a = 5; 
   }, 
   someOtherMethod: function _someOtherMethod (foo, bar, callback) { 
      someOtherClosureVariable.b = 5; 
   }, 
   SubObject: { 
      anotherMethod: function _anotherMethod () { 
         Q.use(someClosureVariable.a); 
      } 
   } 
 }; 
 SomeObject.someMethod.options = { ... }; 
 SomeObject.SubObject.anotherMethod.options = { ... }; 
 </pre> 

 So you would refactor it like this: 

 <pre> 
 SomeObject = Q.Method.define({ 

   
   someMethod: Q.Method.stub, 
   someOtherMethod: Q.Method.stub, 

   
   SubObject: Q.Method.define({ 

      
      anotherMethod: Q.Method.stub 

   
   }, '{{MyPlugin}}/js/methods/SomeObject/SubObject') 

 
 }, '{{MyPlugin}}/js/methods/SomeObject'); 

 // options will be moved to the individual JS files 
 </pre> 

 you would move these methods to a file named @MyPlugin/js/methods/SomeObject/someMethod.js@ like so: 

 <pre> 
 Q.exports(function (someClosureVariable, someOtherClosureVariable) { 

   /** 
    * @class @.Users // make sure YUIDoc categorizes it correctly 
    */ 

   /**  
    * simply copy the whole method and its body here, 
    * including its YUIDoc comments  
    * including the closure references 
    * @static 
    * @method 
    */ 
   return function _someMethod () { 
     someClosureVariable.a = 5; 
   } 

   _someMethod.options = { ... }; 

 }); 
 </pre> 

 Notice that there is even an elegant way to port over the closure variables! However, this only works if the closure variables are basically @const@ which means they never change after they are assigned. That is pretty much the case with nearly all the closure variables in these modules, because they are actually @Object@ which are not reassigned to a different value, but inside, the object's properties can be dynamically changed and that's fine. In other words if you had @const Foo = {}@ you can do @Foo.bar = 'baz'@ with no problem, because the @const@ in Javascript refers to the reference not being reassigned, but the @Foo@ object itself is not "sealed" (which is a different concept in JS). 

 h2. One object at a time 

 So, carefully go method by method, and commit each time you have migrated one object, such as @Q.Users.Web3@ or @Q.Assets.Web3@. Do inner objects and commit before doing outer objects, e.g. (@Q.Assets.NFT.Web3@ before @Q.Assets.NFT@) 

 So, carefully use this mechanism and change one object at a time. Before you commit, you have to make sure everything still works as intended!

Back