lib/soroban/sheet.rb in soroban-0.1.1 vs lib/soroban/sheet.rb in soroban-0.2.0
- old
+ new
@@ -1,8 +1,9 @@
require 'soroban/helpers'
require 'soroban/functions'
-require 'soroban/walker'
+require 'soroban/label_walker'
+require 'soroban/value_walker'
require 'soroban/cell'
module Soroban
# A container for cells.
@@ -25,55 +26,74 @@
end
super
end
# Set the contents of one or more cells or ranges.
- def set(label_or_range, contents)
- unless range = Soroban::getRange(label_or_range)
- return _add(label_or_range, contents)
- end
- fc, fr, tc, tr = 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)
- cc.next! if fr == tr
- cr.next! if fc == tc
+ def set(options_hash)
+ options_hash.each do |label_or_range, contents|
+ unless range = Soroban::getRange(label_or_range)
+ return _add(label_or_range, contents)
end
- raise Soroban::RangeError, "Supplied array doesn't match range length" if cc != tc && cr != tr
- else
- raise ArgumentError, "Can only set cells or 1-dimensional ranges of cells"
+ fc, fr, tc, tr = 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)
+ cc.next! if fr == tr
+ cr.next! if fc == tc
+ end
+ raise Soroban::RangeError, "Supplied array doesn't match range length" if cc != tc && cr != tr
+ else
+ raise ArgumentError, "Can only set cells or 1-dimensional ranges of cells"
+ end
end
end
# Retrieve the contents of a cell.
def get(label_or_name)
- _get(label_or_name, eval("@#{label_or_name}", binding))
+ label = @bindings[label_or_name.to_sym] || label_or_name
+ if Soroban::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(name, label)
- unless @cells.keys.include?(label.to_sym)
- raise Soroban::UndefinedError, "Cannot bind '#{name}' to non-existent cell '#{label}'"
+ def bind(options_hash)
+ options_hash.each do |name, 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)
+ 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)
+ raise Soroban::UndefinedError, "Cannot bind '#{name}' to non-existent cell '#{label_or_range}'"
+ end
+ _bind(name, label_or_range)
+ end
end
- _bind(name, label)
end
# Visit each cell in the supplied range, yielding its value.
def walk(range)
- Walker.new(range, binding)
+ ValueWalker.new(range, binding)
end
# Return a hash of `label => contents` for each cell in the sheet.
def cells
- Hash[@cells.keys.map { |label| label.to_s }.zip( @cells.keys.map { |label| eval("@#{label}.excel") } )]
+ labels = @cells.keys.map { |label| label.to_sym }
+ contents = labels.map { |label| eval("@#{label}.excel") }
+ Hash[labels.zip(contents)]
end
# Return a list of referenced but undefined cells.
def missing
- @cells.values.map.flatten.uniq - @cells.keys
+ @cells.values.flatten.uniq - @cells.keys
end
private
def _add(label, contents)
@@ -82,26 +102,37 @@
cell = Cell.new(binding)
_set(label, cell, contents)
instance_variable_set(internal, cell)
end
- def _set(label, cell, contents)
+ def _set(label_or_name, cell, contents)
+ label = label_or_name.to_sym
+ name = @bindings[label] || label
cell.set(contents)
- @cells[label.to_sym] = cell.dependencies
+ @cells[name] = cell.dependencies
end
def _get(label_or_name, cell)
label = label_or_name.to_sym
- name = @cells[label] ? label : @bindings[label]
+ name = @bindings[label] || label
badref = @cells[name] & missing
raise Soroban::UndefinedError, "Unmet dependencies #{badref.join(', ')} for #{label}" if badref.length > 0
cell.get
end
def _bind(name, label)
@bindings[name.to_sym] = label.to_sym
internal = "@#{label}"
_expose(internal, name)
+ end
+
+ def _bind_range(name, range)
+ @bindings[name.to_sym] = range.to_s
+ instance_eval <<-EOV, __FILE__, __LINE__ + 1
+ def #{name}
+ walk("#{range}")
+ end
+ EOV
end
def _expose(internal, name)
instance_eval <<-EOV, __FILE__, __LINE__ + 1
def #{name}