noflo
Version:
Flow-Based Programming environment for JavaScript
186 lines (157 loc) • 5.86 kB
text/coffeescript
# NoFlo - Flow-Based Programming for JavaScript
# (c) 2013-2017 Flowhub UG
# (c) 2011-2012 Henri Bergius, Nemein
# NoFlo may be freely distributed under the MIT license
#
# The Graph component is used to wrap NoFlo Networks into components inside
# another network.
noflo = require "../lib/NoFlo"
class Graph extends noflo.Component
constructor: (@metadata) ->
@network = null
@ready = true
@started = false
@starting = false
@baseDir = null
@loader = null
@load = 0
@inPorts = new noflo.InPorts
graph:
datatype: 'all'
description: 'NoFlo graph definition to be used with the subgraph component'
required: true
@outPorts = new noflo.OutPorts
@inPorts.graph.on 'ip', (packet) =>
return unless packet.type is 'data'
@setGraph packet.data, (err) =>
# TODO: Port this part to Process API and use output.error method instead
return @error err if err
setGraph: (graph, callback) ->
@ready = false
if typeof graph is 'object'
if typeof graph.addNode is 'function'
# Existing Graph object
@createNetwork graph, callback
return
# JSON definition of a graph
noflo.graph.loadJSON graph, (err, instance) =>
return callback err if err
instance.baseDir = @baseDir
@createNetwork instance, callback
return
if graph.substr(0, 1) isnt "/" and graph.substr(1, 1) isnt ":" and process and process.cwd
graph = "#{process.cwd()}/#{graph}"
noflo.graph.loadFile graph, (err, instance) =>
return callback err if err
instance.baseDir = @baseDir
@createNetwork instance, callback
createNetwork: (graph, callback) ->
@description = graph.properties.description or ''
@icon = graph.properties.icon or @icon
graph.name = @nodeId unless graph.name
graph.componentLoader = @loader
noflo.createNetwork graph, (err, @network) =>
return callback err if err
@emit 'network', @network
# Subscribe to network lifecycle
@subscribeNetwork @network
# Wire the network up
@network.connect (err) =>
return callback err if err
for name, node of @network.processes
# Map exported ports to local component
@findEdgePorts name, node
# Finally set ourselves as "ready"
do @setToReady
do callback
, true
subscribeNetwork: (network) ->
contexts = []
@network.on 'start', =>
ctx = {}
contexts.push ctx
@activate ctx
@network.on 'end', =>
ctx = contexts.pop()
return unless ctx
@deactivate ctx
isExportedInport: (port, nodeName, portName) ->
# First we check disambiguated exported ports
for pub, priv of @network.graph.inports
continue unless priv.process is nodeName and priv.port is portName
return pub
# Then we check ambiguous ports, and if needed, fix them
for exported in @network.graph.exports
continue unless exported.process is nodeName and exported.port is portName
@network.graph.checkTransactionStart()
@network.graph.removeExport exported.public
@network.graph.addInport exported.public, exported.process, exported.port, exported.metadata
@network.graph.checkTransactionEnd()
return exported.public
# Component has exported ports and this isn't one of them
false
isExportedOutport: (port, nodeName, portName) ->
# First we check disambiguated exported ports
for pub, priv of @network.graph.outports
continue unless priv.process is nodeName and priv.port is portName
return pub
# Then we check ambiguous ports, and if needed, fix them
for exported in @network.graph.exports
continue unless exported.process is nodeName and exported.port is portName
@network.graph.checkTransactionStart()
@network.graph.removeExport exported.public
@network.graph.addOutport exported.public, exported.process, exported.port, exported.metadata
@network.graph.checkTransactionEnd()
return exported.public
# Component has exported ports and this isn't one of them
false
setToReady: ->
if typeof process isnt 'undefined' and process.execPath and process.execPath.indexOf('node') isnt -1
process.nextTick =>
@ready = true
@emit 'ready'
else
setTimeout =>
@ready = true
@emit 'ready'
, 0
findEdgePorts: (name, process) ->
# FIXME: direct process.component.inPorts/outPorts access is only for legacy compat
inPorts = process.component.inPorts.ports or process.component.inPorts
outPorts = process.component.outPorts.ports or process.component.outPorts
for portName, port of inPorts
targetPortName = @isExportedInport port, name, portName
continue if targetPortName is false
@inPorts.add targetPortName, port
@inPorts[targetPortName].once 'connect', =>
# Start the network implicitly if we're starting to get data
return if @starting
return if @isStarted()
@start ->
for portName, port of outPorts
targetPortName = @isExportedOutport port, name, portName
continue if targetPortName is false
@outPorts.add targetPortName, port
return true
isReady: ->
@ready
isSubgraph: ->
true
setUp: (callback) ->
@starting = true
unless @isReady()
@once 'ready', =>
@setUp callback
return
return callback null unless @network
@network.start (err) ->
return callback err if err
@starting = false
do callback
tearDown: (callback) ->
@starting = false
return callback null unless @network
@network.stop (err) ->
return callback err if err
do callback
exports.getComponent = (metadata) -> new Graph metadata