-
Notifications
You must be signed in to change notification settings - Fork 55
Module structure
SOCRAT
is designed to be highly scalable. According to large-scale architecture principles, each module implements standard structure. You can add a new module to SOCRAT
in a few easy steps:
Each module is represented by a collection of files, one file per AngularJs
component, such as module, controller, directive, or service.
These components are implemented as CoffeeScript classes in CommonJs module format.
SOCRAT
provides base classes for different types of components, which can be used to create custom implementations via inheritance mechanism.
Services, as well as controllers and directives, are represented by custom classes, which instances will be automatically created later, opposite to module configs, that create module instance from base class instead of inheriting from it.
Let's create new instance of simple module, that doesn't have UI.
###Simple Module Config
We can start by creating a new directory MyModule
under scripts/analysis/
and create MyModule.module.coffee
file inside:
'use strict'
# import module class
Module = require 'scripts/BaseClasses/BaseModule.coffee'
# export instance of new module
module.exports = myModule = new Module
# module id for registration
id: 'socrat_analysis_mymodule'
# module components
components:
services:
'socrat_analysis_mymodule_initService': require 'scripts/analysis/MyModule/MyModuleInit.service.coffee'
'socrat_analysis_mymodule_msgService': require 'scripts/analysis/MyModule/MyModuleMsgService.service.coffee'
'socrat_analysis_mymodule_myService': require 'scripts/analysis/MyModule/MyModuleMyService.service.coffee'
runBlock: require 'scripts/analysis/MyModule/MyModuleRunBlock.run.coffee'
Here we can see that module instance we created contains just a few key items. id
is a string that makes it addressable by SOCRAT
. components
contains imports of all services (also, controllers and directives) and run block of current module by referring to their implementations via require
statement.
First 2 services are required for any module to initialize and communicate. Let's take a closer look on their implementation.
###Init Service
Create MyModuleInit.service.coffee
:
'use strict'
# import base init service class
BaseModuleInitService = require 'scripts/BaseClasses/BaseModuleInitService.coffee'
# export custom init service class
module.exports = class MyModuleInitService extends BaseModuleInitService
# requires injection of message service as a dependency
@inject 'socrat_analysis_mymodule_msgService'
# entry point function:
initialize: ->
# this renaming is required for initialization!
@msgService = @socrat_analysis_mymodule_msgService
# required method call to initiate module messaging interface
@setMsgList()
As you can see it's easy to create Init Service class based on already existing class that hides all necessary functions. Only thing we need to do is to provide a link to messaging service, which is a required dependency. To do that first we need to call base @inject
method with message service id from module config. Then, in the standard initialize
instance method we need to rename messaging service method to @msgService
, such that base class methods can recognize and use it.
As a next step we can look at how to implement messaging service which will allow our module to interact with SOCRAT
.
###Messaging Service
To be able to send and receive data from SOCRAT
every module has use messaging system. For that purpose it's required to have messaging service that provides SOCRAT
with module-specific list of messages, so Core modules
can correctly organize inter-module communications.
Create MyModuleMsgService.service.coffee
file:
'use strict'
# import base messaging module class
BaseModuleMessageService = require 'scripts/BaseClasses/BaseModuleMessageService.coffee'
# export custom messaging service class
module.exports = class MyModuleMsgService extends BaseModuleMessageService
# required to define module message list
msgList:
outgoing: []
incoming: []
# required to be the same as module id
scope: ['socrat_analysis_mymodule']
Again, implementation is very easy since all necessary methods are already are in the base class. Strict requirements here are presence of msgList
with outgoing
, incoming
(both can be empty as long as defined), and scope
which should always contain a string that matches module id
from module config file.
Now we can create custom service that contains main logic of our module.
###Custom Service
Create MyModuleMyService.service.coffee
:
'use strict'
# import base class for data service
BaseService = require 'scripts/BaseClasses/BaseService.coffee'
# export custom data service class
module.exports = class MyModuleMyService extends BaseService
initialize: () ->
showAlert: ->
alert 'I pray Thee, O Developer, that I may be beautiful within.'
This custom service simply defines a method that sends alert message to the screen.
Now we need to create run block to kickstart the module. Note, that if module implements controllers and corresponding routing state, it doesn't require run block and is started via controllers instead (see example below).
###Run Block
Create MyModuleRunBlock.run.coffee
:
'use strict'
module.exports = class MyModuleRunBlock
constructor: (@module) ->
register: ->
@module.run @myModuleRunBlock()
myModuleRunBlock: ->
runBlock = (socrat_analysis_mymodule_myService) ->
socrat_analysis_mymodule_myService.showAlert()
# inject dependencies for run block
runBlock.$inject = ['socrat_analysis_mymodule_myService']
runBlock
Run block file implements a class with an empty constructor, required register
function, and custom run block function that calls showAlert
method of our custom service, that is injected as a dependency.
That's it! In 5 simple files under 10 lines of code each, our custom module is fully functional and is ready to be added to SOCRAT
infrastructure.
###Adding Module to SOCRAT
First, open src/scripts/Socrat/SocratModuleList.coffee
and edit the class SocratModuleList
by adding path to your module config to the analysis
list, e.g.:
analysis: [
# ... already existing objects are here ...
require 'scripts/Database/Database.module.coffee'
,
require 'scripts/analysis/MyModule/MyModule.module.coffee'
,
Tools: [
require 'scripts/analysis/tools/My/Cluster.module.coffee'
]
This will add module config to global config, so SOCRAT
is aware of it.
Now SOCRAT
is ready to be started with your new module. Go ahead and try it!
Let's create new instance of more complicated analysis module, that will appear in main menu under Tools
drop-down. state
is used to add a new state to routing system, identified by corresponding url
and providing Jade templates with module UI.
Extended Module Config
We can start by creating a new directory MyModule
under scripts/analysis/tools
and create MyModule.module.coffee
file inside:
'use strict'
# import module class
Module = require 'scripts/BaseClasses/BaseModule.coffee'
# export instance of new module
module.exports = myModule = new Module
# module id for registration
id: 'socrat_analysis_mymodule'
# module components
components:
services:
'socrat_analysis_mymodule_initService': require 'scripts/analysis/tools/MyModule/MyModuleInit.service.coffee'
'socrat_analysis_mymodule_msgService': require 'scripts/analysis/tools/MyModule/MyModuleMsgService.service.coffee'
'socrat_analysis_mymodule_dataService': require 'scripts/analysis/tools/MyModule/MyModuleDataService.service.coffee'
'socrat_analysis_mymodule_myService': require 'scripts/analysis/tools/MyModule/MyModuleMyService.service.coffee'
controllers:
'mymoduleMainCtrl': require 'scripts/analysis/tools/MyModule/MyModuleMainCtrl.ctrl.coffee'
'mymoduleSidebarCtrl': require 'scripts/analysis/tools/MyModule/MyModuleSidebarCtrl.ctrl.coffee'
directives:
'socratMyModuleDir': require 'scripts/analysis/tools/MyModule/MyModuleDir.directive.coffee'
# module state config
state:
# module name to show in UI
name: 'My Awesome Module'
url: '/tools/mymodule'
mainTemplate: require 'partials/analysis/tools/MyModule/main.jade'
sidebarTemplate: require 'partials/analysis/tools/MyModule/sidebar.jade'
Only thing left is to define messages that our module will send and listen to.
Edit MyModuleMsgService.service.coffee
file:
'use strict'
# import base messaging module class
BaseModuleMessageService = require 'scripts/BaseClasses/BaseModuleMessageService.coffee'
# export custom messaging service class
module.exports = class MyModuleMsgService extends BaseModuleMessageService
# define module message list
msgList:
outgoing: ['mymodule:getData']
incoming: ['mymodule:receiveData']
# required to be the same as module id
scope: ['socrat_analysis_mymodule']
Additionally, SOCRAT
provides a convenience base service class to simplify requesting data inside a module. It can also be created in few lines of code.
###Data Service
Create MyModuleDataService.service.coffee
:
'use strict'
# import base class for data service
BaseModuleDataService = require 'scripts/BaseClasses/BaseModuleDataService.coffee'
# export custom data service class
module.exports = class MyModuleDataService extends BaseModuleDataService
# requires injection of $q and message service
@inject '$q', 'socrat_analysis_mymodule_msgService'
# requires renaming message service injection to @msgService
initialize: () ->
@msgManager = @socrat_analysis_cluster_msgService
# indication of default messages for requesting and receiving data from SOCRAT
@getDataRequest = @msgManager.getMsgList().outgoing[0]
@getDataResponse = @msgManager.getMsgList().incoming[0]
Code is very similar to previous services and includes already familiar initialize
instance method with renaming of messaging service. It also requires to identify which messages from msgList
of MyModuleMsgService
should be used to request and receive data from SOCRAT
.
Then, open src/scripts/Socrat/ScoratMessageMap.coffee
and and edit the class SocratMessageMap
by adding two message redirection paths to @_msgMap
:
module.exports = class SocratMessageMap
constructor: () ->
@_msgMap = [
# ... already existing objects are here ...
,
msgFrom: 'mymodule:getData'
scopeFrom: ['socrat_analysis_mymodule']
msgTo: 'database:getData'
scopeTo: ['socrat_analysis_database']
,
msgFrom: 'database:receiveData'
scopeFrom: ['socrat_analysis_database']
msgTo: 'mymodule:receiveData'
scopeTo: ['socrat_analysis_mymodule']
]
This will tell SOCRAT
to correctly redirect requests from our module to database. It is needed because both our custom module and database module are unaware of each other's existence and we need to provide Core modules
information on how to organize communication between them.