lib/omf_oml/network.rb in omf_oml-0.9.6 vs lib/omf_oml/network.rb in omf_oml-0.9.7
- old
+ new
@@ -5,63 +5,63 @@
require 'omf_oml'
module OMF::OML
-
- class OmlNetworkAlreadyExistException < Exception; end
+
+ class OmlNetworkAlreadyExistException < Exception; end
class OmlNodeAlreadyExistException < Exception; end
- class OmlLinkAlreadyExistException < Exception; end
+ class OmlLinkAlreadyExistException < Exception; end
class UnknownOmlNodeException < Exception; end
-
- class SameNameOnUpdateException < Exception; end
-
+
+ class SameNameOnUpdateException < Exception; end
+
# This class represents a network consisting of nodes and links with their respective
# attributes.
#
class OmlNetwork < OMF::Common::LObject
include MonitorMixin
-
+
@@name2network = {}
-
+
# Return a named network
#
def self.[](name)
@@name2network[name]
end
-
+
attr_reader :name
- #
+ #
# name - Name of table
# opts -
#
def initialize(name = nil, attributes = {})
super name
@name = name || "nw_#{object_id}"
@attributes = attributes
@nodes = {}
@name2node = {}
- @links = {}
+ @links = {}
@name2link = {}
@epoch = 0 # increment whenever an element is being updated
@updateListeners = {}
if name
synchronize do
if @@name2network[name]
raise OmlNetworkAlreadyExistException.new(name)
end
@@name2network[name] = self
end
- end
+ end
end
-
+
def nodes()
@nodes.values
end
-
+
# Return the node named +name+. If the node doesn't exist and
# +new_opts+ is a Hash, create a new one and return that.
#
def node(name, new_opts = nil)
return name if name.kind_of? NetworkNode
@@ -73,11 +73,11 @@
end
def links()
@links.values
end
-
+
# Return the link named +name+. If the link doesn't exist and
# +new_opts+ is a Hash, create a new one and return that.
#
def link(name, new_opts = nil)
return name if name.kind_of? NetworkLink
@@ -86,11 +86,11 @@
link = create_link(name, nil, nil, new_opts)
end
link
end
-
+
# Register a callback to be called every time network elements change
# The callback is provided with an arrach of changed elements.
#
def on_update(name = :_, &callback)
if (callback)
@@ -100,11 +100,11 @@
@updateListeners[name] = callback
else
@updateListeners.delete(name)
end
end
-
+
# NOTE: May need a monitor if used in multi-threaded environments
#
def create_node(name = nil, attributes = {})
name = name.to_sym if name
synchronize do
@@ -115,22 +115,22 @@
@nodes[node.el_id] = node
@name2node[name] = node if name
node
end
end
-
+
#
# opts
# :from - fromNode if +fromNode+ is nil
# :to - toNode if +toNode+ is nil
# ... - rest of options passed on to +NetworkLink+ constructor
- #
+ #
def create_link(name = nil, fromNode = nil, toNode = nil, attributes = {})
name = name.to_sym if name
fromNode = attributes.delete(:from) unless fromNode
toNode = attributes.delete(:to) unless toNode
-
+
synchronize do
if name && @name2link[name]
raise OmlLinkAlreadyExistException.new(name)
end
if fromNode
@@ -143,40 +143,40 @@
@links[link.el_id] = link
@name2link[name] = link if name
link
end
end
-
+
# To have the update listeners only called once when multiple elements are changed at once, perform the
# changes within a +transaction+ block. The listeners are then called once with an array containing
# all updated elements.
- #
+ #
def transaction(&block)
updated = UpdateSet.new
synchronize do
@updated = updated
@in_transaction = true
block.call
- @in_transaction = true
+ @in_transaction = true
end
unless updated.empty?
@updateListeners.values.each do |l|
l.call(updated)
end
end
end
-
+
def node_schema(schema = nil)
if schema
@node_schema = OmlSchema.create(schema)
@node_schema.insert_column_at(0, :id)
@node_schema.insert_column_at(1, :name)
end
@node_schema
end
-
+
def link_schema(schema = nil)
if schema
@link_schema = OmlSchema.create(schema)
@link_schema.insert_column_at(0, :id)
@link_schema.insert_column_at(1, :name)
@@ -184,33 +184,33 @@
@link_schema.insert_column_at(3, :to_id)
end
@link_schema
end
-
+
def describe
nh = {}
@nodes.each do |id, node| nh[id] = node.describe end
lh = {}
@links.each do |id, link| lh[id] = link.describe end
- {:nodes => nh, :links => lh}
+ {:nodes => nh, :links => lh}
end
-
+
# Creates two tables, one capturing the link state and one for the node state.
# Returns the two tables in a hash with keys 'nodes' and 'links'.
#
# def to_tables(table_opts = {})
# node_table = OmlTable.new 'nodes', @node_schema, table_opts
# @nodes.each do |id, n|
# node_table.add_row @node_schema.hash_to_row(n.attributes)
# end
-#
+#
# link_table = OmlTable.new 'links', @link_schema, table_opts
# @links.each do |id, l|
# link_table.add_row @link_schema.hash_to_row(l.attributes)
# end
-#
+#
# on_update "__to_tables_#{node_table.object_id}" do |a|
# a.each do |e|
# if e.kind_of? NetworkNode
# node_table.add_row @node_schema.hash_to_row(e.attributes)
# else
@@ -219,97 +219,105 @@
# end
# end
# {:nodes => node_table, :links => link_table}
# #{:nodes => to_table(:nodes, table_opts), :links => to_table(:links, table_opts)}
# end
-
- # Create a table to track an aspect of this network.
+
+ # Create a table to track an aspect of this network.
#
- # aspect - Either :nodes or :links
+ # aspect - Either :nodes or :links
#
def to_table(aspect, table_opts = {})
aspect = aspect.to_sym
case aspect
when :nodes
table = OmlTable.create @name + '/nodes', @node_schema, table_opts
+ #puts "TABLE SCHEME >>>> #{table.schema}"
table.add_rows(@nodes.map do |id, n|
@node_schema.hash_to_row(n.attributes)
end)
on_update "__to_tables_nodes_#{table.object_id}" do |a|
nodes = a.map do |e|
e.kind_of?(NetworkNode) ? @node_schema.hash_to_row(e.attributes) : nil
end.compact
table.add_rows(nodes) unless nodes.empty?
- end
-
+ end
+
when :links
table = OmlTable.create @name + '/links', @link_schema, table_opts
# @links.each do |id, l|
# table.add_row @link_schema.hash_to_row(l.attributes)
# end
table.add_rows(@links.map do |id, n|
+ # puts @link_schema.hash_to_row(n.attributes).inspect
+ # puts table.schema
+ # table.add_row @link_schema.hash_to_row(n.attributes)
+ # puts table.rows.inspect
+ # exit
+
@link_schema.hash_to_row(n.attributes)
end)
+ #puts table.rows.inspect
on_update "__to_tables_links_#{table.object_id}" do |a|
links = a.map do |e|
e.kind_of?(NetworkLink) ? @link_schema.hash_to_row(e.attributes) : nil
end.compact
table.add_rows(links) unless links.empty?
- end
-
+ end
+
else
raise "Unknown aspect '#{aspect}'. Should be either 'nodes' or 'links'."
end
-
+
# on_update "__to_tables_#{table.object_id}" do |a|
# a.each do |e|
# if aspect == :nodes && e.kind_of?(NetworkNode)
# table.add_row @node_schema.hash_to_row(e.attributes)
# end
# if aspect == :links && e.kind_of?(NetworkLink)
# table.add_row @link_schema.hash_to_row(e.attributes)
# end
# end
# end
-
+
table
end
-
-
+
+
def to_json
describe.to_json
end
-
-
-
+
+
+
def updated(element)
synchronize do
if @in_transaction
@updated << element
- return
+ return
end
end
uset = UpdateSet.new
uset << element
@updateListeners.each do |l|
l.call(uset)
end
end
end # OMLNetwork
-
- class NetworkElementAttributeException < Exception; end
-
+ class NetworkElementAttributeException < Exception; end
+
+
# This class represents an abstract network element and shouldn't be used directly.
#
class NetworkElement < OMF::Common::LObject
attr_reader :name
attr_reader :el_id
attr_reader :attributes
-
+
def initialize(name, attributes, network)
super name
@attributes = attributes.dup
if @name = name
@attributes[:name] = name
@@ -320,148 +328,148 @@
@el_id = @attributes[:id] = "e#{self.object_id}"
@attributes[:name] = name || @el_id
@network = network
end
-
+
def [](name)
@attributes[name]
end
-
+
def []=(name, value)
@attributes[name] = _set(value, @attributes[name])
end
-
+
# Update the element's attributes. The +attributes+ argument
# is expected to be a hash with the key identifying the attribute
# name and the value being the new value to set that attribute to.
#
def update(attributes)
attributes.each do |name, value|
self[name] = value
end
end
-
+
# Return the current state of the network element as hash
#
def describe
- @attributes
+ @attributes
end
-
+
def node?
false
end
-
+
def link?
false
end
-
+
protected
-
+
def _set(value, old_value)
if value != old_value
@network.updated(self)
end
value
end
-
- end # NetworkElement
-
+
+ end # NetworkElement
+
# This class represents a network node. Should NOT be created directly, but only through
# +OmlNetwork#create_node+ method
#
class NetworkNode < NetworkElement
-
+
def initialize(name, attributes, network)
super
end
-
+
def node?
true
end
- end # NetworkNode
+ end # NetworkNode
- # This class represents a network link between two nodes.
+ # This class represents a network link between two nodes.
# Should NOT be created directly, but only through
# +OmlNetwork#create_node+ method
#
class NetworkLink < NetworkElement
attr_reader :from # node
attr_reader :to # node
-
+
def initialize(name, fromNode, toNode, attributes, network)
super name, attributes, network
if fromNode
@fromNode = fromNode
#puts ">>>> NODE: #{fromNode.inspect}"
@attributes[:from_id] = fromNode.el_id
end
- if toNode
+ if toNode
@toNode = toNode
@attributes[:to_id] = toNode.el_id
end
end
-
+
def from=(node)
- @attributes[:from_id] = node.el_id if node
+ @attributes[:from_id] = node.el_id if node
@fromNode = _set(node, @fromNode)
end
def to=(node)
- @attributes[:to_id] = node.el_id if node
+ @attributes[:to_id] = node.el_id if node
@toNode = _set(node, @toNode)
end
-
+
def link?
true
end
- end # NetworkLink
+ end # NetworkLink
# This set may hold a set of nodes and links which have been
# updated during a transaction. It supports the +describe+
# function which returns a domain-specific combine of all the
# included network elements.
#
class UpdateSet < Set
def describe()
nh = {}
lh = {}
-
- self.each do |el|
+
+ self.each do |el|
d = el.describe
if el.kind_of? NetworkNode
nh[el.el_id] = d
else
lh[el.el_id] = d
- end
+ end
end
- {:nodes => nh, :links => lh}
+ {:nodes => nh, :links => lh}
end
end
end
if $0 == __FILE__
require 'json'
include OMF::Common::OML
-
+
nw = OmlNetwork.new
-
+
cnt = 3
- cnt.times do |i|
+ cnt.times do |i|
nw.create_node "n#{i}", :x => i
end
- cnt.times do |i|
+ cnt.times do |i|
nw.create_link "l#{i}", "n#{i}", "n#{(i + 1) % cnt}", :y => i
end
-
+
puts nw.describe.to_json
-
+
nw.on_update do |els|
puts "UPDATED: #{els}"
end
nw.nodes.first[:x] = 20
-
- nw.transaction do
+
+ nw.transaction do
nw.nodes.first[:x] = 30
- nw.links.first[:y] = 20
+ nw.links.first[:y] = 20
end
-end
+end