lib/soroban/sheet.rb in soroban-0.5.4 vs lib/soroban/sheet.rb in soroban-0.7.2
- old
+ new
@@ -1,25 +1,80 @@
require 'soroban/helpers'
require 'soroban/functions'
require 'soroban/label_walker'
require 'soroban/value_walker'
+require 'soroban/tabulator'
require 'soroban/cell'
+require 'set'
+
module Soroban
# A container for cells.
class Sheet
attr_reader :bindings
# Creates a new sheet.
def initialize(logger=nil)
@logger = logger
@cells = {}
+ @compiled = {}
@changes = Hash.new{ |h, k| h[k] = Set.new }
@bindings = {}
end
+ def factory(name)
+ eval(self.to_ruby(name), TOPLEVEL_BINDING)
+ Object::const_get('Soroban').const_get('Model').const_get(name).new
+ end
+
+ # Return a string containing a ruby class that implements the sheet. You can
+ # call eval() on this string to create the class, which you can then
+ # instantiate. Set inputs on the instance and read outputs off.
+ def to_ruby(class_name)
+ data = []
+ data << "module Soroban"
+ data << "module Model"
+ data << "class #{class_name}"
+ data << " def initialize"
+ data << " @binds = {"
+ data << bindings.map do |name, cell|
+ " '#{name}' => :#{cell}"
+ end.join(",\n")
+ data << " }"
+ data << " @cache = {}"
+ data << " @cells = {"
+ data << @compiled.map do |label, cell|
+ " :#{label} => lambda { @cache[:#{label}] ||= #{cell.to_compiled_ruby} }"
+ end.join(",\n")
+ data << " }"
+ data << " end"
+ data << " def clear"
+ data << " @cache.clear"
+ data << " end"
+ data << " def get(name)"
+ data << " @cells[@binds[name]].call"
+ data << " end"
+ data << " def set(name, value)"
+ data << " self.clear"
+ data << " @cells[@binds[name]] = lambda { @cache[@binds[name]] ||= value }"
+ data << " end"
+ bindings.each do |name, cell|
+ data << " def #{name}"
+ data << " get('#{name}')"
+ data << " end"
+ data << " def #{name}=(value)"
+ data << " set('#{name}', value)"
+ data << " end"
+ end
+ data << "end"
+ data << "end"
+ data << "end"
+ puts data.join("\n")
+ data.join("\n")
+ end
+
# Used for calling dynamically defined functions, and for creating new
# cells (via `label=`).
def method_missing(method, *args, &block)
if match = /^func_(.*)$/i.match(method.to_s)
return Soroban::call(self, match[1], *args)
@@ -123,9 +178,10 @@
return
end
internal = "@#{label}"
_expose(internal, label)
cell = Cell.new(binding)
+ @compiled[label] = cell
_set(label, cell, contents)
instance_variable_set(internal, cell)
end
def _set(label_or_name, cell, contents)