Class: Soroban::Sheet

Inherits:
Object
  • Object
show all
Defined in:
lib/soroban/sheet.rb

Overview

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.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(logger = nil) ⇒ Sheet

Creates a new sheet.



20
21
22
23
24
25
# File 'lib/soroban/sheet.rb', line 20

def initialize(logger=nil)
  @_logger = logger
  @_cells = {}
  @_changes = Hash.new { |h, k| h[k] = Set.new }
  @bindings = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

Used for calling dynamically defined functions, and for creating new cells (via `label=`).



29
30
31
32
33
34
35
36
# File 'lib/soroban/sheet.rb', line 29

def method_missing(method, *args, &block)
  if match = /^func_(.*)$/i.match(method.to_s)
    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

Instance Attribute Details

#bindingsObject (readonly)

Returns the value of attribute bindings



17
18
19
# File 'lib/soroban/sheet.rb', line 17

def bindings
  @bindings
end

Instance Method Details

#bind(options_hash) ⇒ Object

Bind one or more named variables to a cell.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/soroban/sheet.rb', line 74

def bind(options_hash)
  options_hash.each do |name, label_or_range|
    _debug("binding '#{name}' to '#{label_or_range}'}")
    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.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

#cellsObject

Return a hash of `label => contents` for each cell in the sheet.



98
99
100
101
102
# File 'lib/soroban/sheet.rb', line 98

def cells
  labels = @_cells.keys.map(&:to_sym)
  contents = labels.map { |label| eval("@#{label}.excel") }
  Hash[labels.zip(contents)]
end

#get(label_or_name) ⇒ Object

Retrieve the contents of a cell.



63
64
65
66
67
68
69
70
71
# File 'lib/soroban/sheet.rb', line 63

def get(label_or_name)
  label = @bindings[label_or_name.to_sym] || label_or_name
  _debug("retrieving '#{label_or_name}' from '#{label}'}")
  if Soroban::Helpers.range?(label)
    walk(label)
  else
    _get(label_or_name, eval("@#{label}", binding))
  end
end

#missingObject

Return an array of referenced but undefined cells.



105
106
107
# File 'lib/soroban/sheet.rb', line 105

def missing
  (@_cells.values.reduce(:|) - @_cells.keys).to_a
end

#set(options_hash) ⇒ Object

Set the contents of one or more cells or ranges.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/soroban/sheet.rb', line 39

def set(options_hash)
  options_hash.each do |label_or_range, contents|
    _debug("setting '#{label_or_range}' to '#{contents}'")
    unless Soroban::Helpers.range?(label_or_range)
      _add(label_or_range, contents)
      next
    end
    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)
        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

#walk(range) ⇒ Object

Visit each cell in the supplied range, yielding its value.



93
94
95
# File 'lib/soroban/sheet.rb', line 93

def walk(range)
  Soroban::ValueWalker.new(range, binding)
end