lib/soroban/sheet.rb in soroban-0.7.3 vs lib/soroban/sheet.rb in soroban-0.8.0
- old
+ new
@@ -1,47 +1,51 @@
+unless defined?(Set)
+ require 'set'
+end
+
+require 'soroban/errors'
require 'soroban/helpers'
require 'soroban/functions'
+require 'soroban/cell'
require 'soroban/label_walker'
require 'soroban/value_walker'
-require 'soroban/cell'
-require 'set'
-
module Soroban
- # A container for cells.
+ # A container for cells. This is what the end user of Soroban will manipulate,
+ # either directly or via an importer that returns a Sheet instance.
class Sheet
attr_reader :bindings
# Creates a new sheet.
def initialize(logger=nil)
- @logger = logger
- @cells = {}
- @changes = Hash.new{ |h, k| h[k] = Set.new }
+ @_logger = logger
+ @_cells = {}
+ @_changes = Hash.new { |h, k| h[k] = Set.new }
@bindings = {}
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)
+ return Soroban::Functions.call(self, match[1], *args)
elsif match = /^([a-z][\w]*)=$/i.match(method.to_s)
return _add(match[1], args[0])
end
super
end
# Set the contents of one or more cells or ranges.
def set(options_hash)
options_hash.each do |label_or_range, contents|
_debug("setting '#{label_or_range}' to '#{contents}'")
- unless range = Soroban::getRange(label_or_range)
+ unless Soroban::Helpers.range?(label_or_range)
_add(label_or_range, contents)
next
end
- fc, fr, tc, tr = range
+ fc, fr, tc, tr = Soroban::Helpers.getRange(label_or_range)
if fc == tc || fr == tr
raise ArgumentError, "Expecting an array when setting #{label_or_range}" unless contents.kind_of? Array
cc, cr = fc, fr
contents.each do |item|
set("#{cc}#{cr}" => item)
@@ -57,66 +61,66 @@
# Retrieve the contents of a cell.
def get(label_or_name)
label = @bindings[label_or_name.to_sym] || label_or_name
_debug("retrieving '#{label_or_name}' from '#{label}'}")
- if Soroban::range?(label)
+ if Soroban::Helpers.range?(label)
walk(label)
else
_get(label_or_name, eval("@#{label}", binding))
end
end
# Bind one or more named variables to a cell.
def bind(options_hash)
options_hash.each do |name, label_or_range|
_debug("binding '#{name}' to '#{label_or_range}'}")
- if Soroban::range?(label_or_range)
- LabelWalker.new(label_or_range).each do |label|
- next if @cells.keys.include?(label.to_sym)
+ if Soroban::Helpers.range?(label_or_range)
+ Soroban::LabelWalker.new(label_or_range).each do |label|
+ next if @_cells.has_key?(label.to_sym)
raise Soroban::UndefinedError, "Cannot bind '#{name}' to range '#{label_or_range}'; cell #{label} is not defined"
end
_bind_range(name, label_or_range)
else
- unless @cells.keys.include?(label_or_range.to_sym)
+ unless @_cells.has_key?(label_or_range.to_sym)
raise Soroban::UndefinedError, "Cannot bind '#{name}' to non-existent cell '#{label_or_range}'"
end
_bind(name, label_or_range)
end
end
end
# Visit each cell in the supplied range, yielding its value.
def walk(range)
- ValueWalker.new(range, binding)
+ Soroban::ValueWalker.new(range, binding)
end
# Return a hash of `label => contents` for each cell in the sheet.
def cells
- labels = @cells.keys.map { |label| label.to_sym }
+ labels = @_cells.keys.map(&:to_sym)
contents = labels.map { |label| eval("@#{label}.excel") }
Hash[labels.zip(contents)]
end
- # Return a list of referenced but undefined cells.
+ # Return an array of referenced but undefined cells.
def missing
- @cells.values.flatten.uniq - @cells.keys
+ (@_cells.values.reduce(:|) - @_cells.keys).to_a
end
private
def _debug(message)
- return if @logger.nil?
- @logger.debug "SOROBAN: #{message}"
+ return if @_logger.nil?
+ @_logger.debug "SOROBAN: #{message}"
end
def _link(name, dependencies)
- dependencies.each { |target| @changes[target] << name if name != target }
+ dependencies.each { |target| @_changes[target] << name if name != target }
end
def _unlink(name, dependencies)
- dependencies.each { |target| @changes[target].delete(name) }
+ dependencies.each { |target| @_changes[target].delete(name) }
end
def _add(label, contents)
name = @bindings[label.to_sym] || label
if cells.has_key?(name)
@@ -124,28 +128,28 @@
cell.set(contents)
return
end
internal = "@#{label}"
_expose(internal, label)
- cell = Cell.new(binding)
+ cell = Soroban::Cell.new(binding)
_set(label, cell, contents)
instance_variable_set(internal, cell)
end
def _set(label_or_name, cell, contents)
label = label_or_name.to_sym
name = @bindings[label] || label
_unlink(name, cell.dependencies)
cell.set(contents)
- @cells[name] = cell.dependencies
+ @_cells[name] = cell.dependencies
_link(name, cell.dependencies)
_clear(name)
end
def _clear(name)
- @changes[name].each do |target|
- next unless @cells.has_key?(target)
+ @_changes[name].each do |target|
+ next unless @_cells.has_key?(target)
begin
eval("@#{target.to_s}.clear")
_clear(target)
rescue
end
@@ -153,11 +157,11 @@
end
def _get(label_or_name, cell)
label = label_or_name.to_sym
name = @bindings[label] || label
- badref = @cells[name] & missing
- raise Soroban::UndefinedError, "Unmet dependencies #{badref.join(', ')} for #{label}" if badref.length > 0
+ badref = @_cells[name] & missing
+ raise Soroban::UndefinedError, "Unmet dependencies #{badref.to_a.join(', ')} for #{label}" if badref.length > 0
cell.get
end
def _bind(name, label)
@bindings[name.to_sym] = label.to_sym