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)