Best way to program an "Excel-like" network of influence in Ruby?

741 Views Asked by At

I have a network of nodes, each node influencing the state of some other nodes (imagine an Excel spreadsheet with cells values depending on other cells through formulas).

I'm wondering what is the cleanest way to implement this in Ruby ?

Of course I could have one process per node, but how will it perform if the number of nodes increases ? And, I'm sure there are libraries for that, but I can't find a up-to-date one.

Thanks for your help !

Update: Sounds like EventMachine might do the job... but it seems more adapted to a small number of "nodes"

3

There are 3 best solutions below

0
On

This sounds like a good situation for the observer pattern. This is a sample of that in ruby:

require 'observer'

class Node
  attr_accessor :id
  @@current_node_id = 0
  def initialize
    @@current_node_id += 1
    id = @@current_node_id
  end
  include Observable

  attr_reader :value


  protected
  def value=(new_value)
    return if @value == new_value
    old_value = @value
    @value = new_value
    changed
    notify_observers(id, old_value, @value)
  end
end


class ValueNode < Node
  def initialize(initial_value)
    super()
    @value = initial_value
  end

  def value=(new_value)
    super(new_value)
  end
end


class SumNode < Node
  def initialize(*nodes)
    super()
    @value = nodes.map(&:value).inject(0, &:+)
    nodes.each do |node|
      node.add_observer(self)
    end
  end


  def update(id, old_value, new_value)
    self.value = self.value - old_value + new_value
  end
end


def test
  v1 = ValueNode.new 4
  v2 = ValueNode.new 8
  sum = SumNode.new(v1, v2)
  sum2 = SumNode.new(v1, sum)
  v2.value = 10
  p sum.value
  p sum2.value
end


test()

Notice how the value of SumNode isn't recalculated every time it is requested - instead it is updated when one of its value nodes is updated. This works recursively, so that inner SumNodes also trigger updates. As the notification includes the unique id of the node, it is possible to write more complex Node types, such as ones that contain formulas.

See http://www.ruby-doc.org/stdlib/libdoc/observer/rdoc/index.html for more details on Observable

0
On

This sounds similar to the oft-used Twitter paradigm, where updates by one user are pushed to all it's followers. To do this efficiently, you should store two lists for a given person: one with the people he follows and one with the people that follow him. You can do the same for a list of nodes. When a node changes you can quickly look up the nodes that are influenced by this node. When a relationship disappears you will need the 'forward' list to know from which lists to 'remove' the reverse relationship.

You can store these lists two-dimensional arrays, or in something like Redis. I don't really understand how EventMachine would fit in.

0
On

If you have a network graph of dependencies and you want them to scale, a graph database is the best solution. Neo4J is a popular, powerful database for tracking this type of dependencies.

There are several ways to interface with Neo4J from Ruby: