Skip to content
Alexandr Kalinin edited this page Aug 17, 2016 · 7 revisions

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:

Module template

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.

Clone this wiki locally