soroban 0.7.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,9 @@
1
+ require 'soroban/errors'
2
+ require 'soroban/label_walker'
3
+
1
4
  module Soroban
2
5
 
3
- # An enumerable that allows cells in a range to be visited.
6
+ # An enumerable that allows values of cells in a range to be visited.
4
7
  class ValueWalker
5
8
 
6
9
  include Enumerable
@@ -8,40 +11,37 @@ module Soroban
8
11
  # Create a new walker from a supplied range and binding. The binding is
9
12
  # required when calculating the value of each visited cell.
10
13
  def initialize(range, context)
11
- @range, @binding = range, context
12
- @walker = LabelWalker.new(range)
14
+ @_range, @_binding = range, context
15
+ @_labels = Soroban::LabelWalker.new(range).to_a
13
16
  end
14
17
 
15
18
  # Yield the value of each cell referenced by the supplied range.
16
19
  def each
17
- @walker.each { |label| yield eval("get('#{label}')", @binding) }
20
+ @_labels.each { |label| yield eval("get('#{label}')", @_binding) }
18
21
  end
19
22
 
20
- # Retrieve the value of a cell within the range by index
23
+ # Get the value of a cell within the range by index. Will raise a RangeError
24
+ # if the index is outside of the range.
21
25
  def [](index)
22
- labels = @walker.to_a
23
- if index < 0 || index >= labels.length
24
- raise Soroban::RangeError, "Index #{index} falls outside of '#{@range}'"
26
+ if index < 0 || index >= @_labels.length
27
+ raise Soroban::RangeError, "Index #{index} falls outside of '#{@_range}'"
25
28
  end
26
- eval("get('#{labels[index]}')", @binding)
29
+ eval("get('#{@_labels[index]}')", @_binding)
27
30
  end
28
31
 
29
- # Set the value of a cell within the range by index
32
+ # Set the value of a cell within the range by index. Will raise a RangeError
33
+ # if the index is outside of the range.
30
34
  def []=(index, value)
31
- count = 0
32
- @walker.each do |label|
33
- if index == count
34
- eval("@#{label}.set('#{value}')", @binding)
35
- return value
36
- end
37
- count += 1
35
+ if index < 0 || index >= @_labels.length
36
+ raise Soroban::RangeError, "Index #{index} falls outside of '#{@_range}'"
38
37
  end
39
- raise Soroban::RangeError, "Index #{index} falls outside of '#{@range}'"
38
+ eval("@#{@_labels[index]}.set('#{value}')", @_binding)
39
+ return value
40
40
  end
41
41
 
42
42
  # Display the range if the user outputs the binding directly
43
43
  def to_s
44
- @range
44
+ @_range
45
45
  end
46
46
  alias inspect to_s
47
47
 
@@ -2,93 +2,81 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "Documentation" do
4
4
 
5
- it "has documentation that works as advertised" do
6
-
7
- # Example Usage
8
-
9
- s = Soroban::Sheet.new()
5
+ let(:s) { Soroban::Sheet.new }
10
6
 
7
+ it "usage works" do
11
8
  s.A1 = 2
12
9
  s.set('B1:B5' => [1,2,3,4,5])
13
10
  s.C1 = "=SUM(A1, B1:B5, 5) + A1 ^ 3"
14
11
  s.C2 = "=IF(C1>30,'Large','Tiny')"
15
12
 
16
- puts s.C1 # => 30
13
+ # puts s.C1 # => 30
17
14
  s.C1.should eq(30)
18
15
 
19
16
  s.bind(:input => :A1, :output => :C2)
20
17
 
21
- puts s.output # => "Tiny"
18
+ # puts s.output # => "Tiny"
22
19
  s.output.should eq('Tiny')
23
20
 
24
21
  s.input = 3
25
22
 
26
- puts s.output # => "Large"
23
+ # puts s.output # => "Large"
27
24
  s.output.should eq('Large')
28
- puts s.C1 # => 50
25
+ # puts s.C1 # => 50
29
26
  s.C1.should eq(50)
27
+ end
30
28
 
31
- # Bindings
32
-
33
- s = Soroban::Sheet.new()
34
-
29
+ it "bindings work" do
35
30
  s.set(:A1 => 'hello', 'B1:B5' => [1,2,3,4,5])
36
31
 
37
32
  s.bind(:foo => :A1, :bar => 'B1:B5')
38
33
 
39
- puts s.foo # => 'hello'
34
+ # puts s.foo # => 'hello'
40
35
  s.foo.should eq('hello')
41
- puts s.bar[0] # => 1
36
+ # puts s.bar[0] # => 1
42
37
  s.bar[0].should eq(1)
43
38
 
44
39
  s.bar[0] = 'howdy'
45
- s.bar[0].should eq('howdy')
46
-
47
- puts s.B1 # => 'howdy'
40
+ # puts s.B1 # => 'howdy'
48
41
  s.B1.should eq('howdy')
42
+ end
49
43
 
50
- # Persistence
51
-
52
- s = Soroban::Sheet.new()
53
-
44
+ it "persistence works" do
54
45
  s.F1 = "= E1 + SUM(D1:D5)"
55
46
 
56
- s.missing # => [:E1, :D1, :D2, :D3, :D4, :D5]
57
- expected = [:E1, :D1, :D2, :D3, :D4, :D5]
58
- s.missing.should =~ expected
47
+ # s.missing # => [:E1, :D1, :D2, :D3, :D4, :D5]
48
+ s.missing.should =~ [:E1, :D1, :D2, :D3, :D4, :D5]
59
49
 
60
50
  s.E1 = "= D1 ^ D2"
61
51
  s.set("D1:D5" => [1,2,3,4,5])
62
52
 
63
- s.missing # => []
64
- expected = []
65
- s.missing.should =~ expected
66
-
67
- s.cells # => {"F1"=>"= E1 + SUM(D1:D5)", "E1"=>"= D1 ^ D2", "D1"=>"1", "D2"=>"2", "D3"=>"3", "D4"=>"4", "D5"=>"5"}
68
-
69
- # Importers
70
-
71
- # (TBD)
53
+ # s.missing # => []
54
+ s.missing.should =~ []
72
55
 
73
- # Iteration
56
+ # s.cells # => {:F1=>"= E1 + SUM(D1:D5)", :E1=>"= D1 ^ D2", :D1=>"1", :D2=>"2", :D3=>"3", :D4=>"4", :D5=>"5"}
57
+ s.cells.keys.should =~ [:F1, :E1, :D1, :D2, :D3, :D4, :D5]
58
+ s.cells[:D3].should eq("3")
59
+ end
74
60
 
61
+ it "iteration works" do
75
62
  s.set('D1:D5' => [1,2,3,4,5])
76
- s.walk('D1:D5').reduce(:+) # => 15
63
+ # s.walk('D1:D5').reduce(:+) # => 15
77
64
  s.walk('D1:D5').reduce(:+).should eq(15)
65
+ end
78
66
 
79
- # Functions
80
-
81
- Soroban::functions # => ["MIN", "VLOOKUP", "AND", "MAX", "OR", "NOT", "IF", "AVERAGE", "SUM"]
67
+ it "functions work" do
68
+ # Soroban::functions # => ["AND", "AVERAGE", "EXP", "IF", "LN", "MAX", "MIN", "NOT", "OR", "SUM", "VLOOKUP"]
69
+ Soroban::Functions.all.should =~ ["AND", "AVERAGE", "EXP", "IF", "LN", "MAX", "MIN", "NOT", "OR", "SUM", "VLOOKUP"]
82
70
 
83
- Soroban::define :FOO => lambda { |lo, hi|
71
+ Soroban::Functions.define :FOO => lambda { |lo, hi|
84
72
  raise ArgumentError if lo > hi
85
- rand(hi-lo) + lo
73
+ rand(hi-lo) + lo
86
74
  }
87
75
 
88
76
  s.g = "=FOO(10, 20)"
89
77
 
90
- puts s.g # => 17
91
-
78
+ # puts s.g # => 17
79
+ s.g.between?(10, 20).should be_true
92
80
  end
93
81
 
94
82
  end
@@ -1,33 +1,25 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- has_rubyxl = begin
4
- Gem::Specification::find_by_name("rubyXL")
5
- rescue Gem::LoadError
6
- false
7
- end
8
-
9
- describe "Documentation", :if => has_rubyxl do
10
-
11
- it "can import xlsx files using RubyXL" do
3
+ describe "Documentation", :if => defined?(RubyXL) do
12
4
 
5
+ it "importers work" do
13
6
  BINDINGS = {
14
7
  :planet => :B1,
15
8
  :mass => :B2,
16
9
  :force => :B3
17
10
  }
18
11
 
19
- s = Soroban::Import::rubyXL("files/Physics.xlsx", 0, BINDINGS )
12
+ s = Soroban::Import::rubyXL("files/Physics.xlsx", 0, BINDINGS)
20
13
 
21
14
  s.planet = 'Earth'
22
15
  s.mass = 80
23
- puts s.force # => 783.459251241996
16
+ # puts s.force # => 783.459251241996
24
17
  s.force.should be_within(0.01).of(783.46)
25
18
 
26
19
  s.planet = 'Venus'
27
20
  s.mass = 80
28
- puts s.force # => 710.044826106394
21
+ # puts s.force # => 710.044826106394
29
22
  s.force.should be_within(0.01).of(710.04)
30
-
31
23
  end
32
24
 
33
25
  end
@@ -102,15 +102,19 @@ describe "Soroban" do
102
102
  end
103
103
 
104
104
  it "can define new functions" do
105
- Soroban::define :FOO => lambda { |a, b| 2 * a + b / 2 }
105
+ Soroban::Functions.define :FOO => lambda { |a, b| 2 * a + b / 2 }
106
106
  sheet.A1 = 7
107
107
  sheet.A2 = 8
108
108
  sheet.A3 = "=foo(A1, A2)"
109
109
  sheet.A3.should eq(18)
110
- Soroban::functions.should include 'FOO'
110
+ Soroban::Functions.all.should include 'FOO'
111
111
  end
112
112
 
113
113
  it "can report on missing cells" do
114
+ sheet.A3 = "=A2"
115
+ expected = [:A2]
116
+ sheet.missing.should =~ expected
117
+
114
118
  sheet.A3 = "=A2+foo(A3:B4)"
115
119
  expected = [:A2, :A4, :B3, :B4 ]
116
120
  sheet.missing.should =~ expected
@@ -1,6 +1,14 @@
1
1
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
2
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
3
4
  require 'rspec'
5
+
6
+ begin
7
+ require 'RubyXL'
8
+ rescue LoadError
9
+ # it is OK for the user not to have RubyXL
10
+ end
11
+
4
12
  require 'soroban'
5
13
 
6
14
  # Requires supporting files with custom matchers and macros, etc,
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: soroban
3
3
  version: !ruby/object:Gem::Version
4
- hash: 5
4
+ hash: 63
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 7
9
- - 3
10
- version: 0.7.3
8
+ - 8
9
+ - 0
10
+ version: 0.8.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jason Hutchens
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2014-03-20 00:00:00 Z
18
+ date: 2014-03-25 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: treetop
@@ -105,7 +105,7 @@ files:
105
105
  - files/Physics.xlsx
106
106
  - lib/soroban.rb
107
107
  - lib/soroban/cell.rb
108
- - lib/soroban/error.rb
108
+ - lib/soroban/errors.rb
109
109
  - lib/soroban/functions.rb
110
110
  - lib/soroban/functions/and.rb
111
111
  - lib/soroban/functions/average.rb
@@ -1,20 +0,0 @@
1
- module Soroban
2
-
3
- # Thrown if an invalid formula is assigned to a cell.
4
- class ParseError < StandardError
5
- end
6
-
7
- # Thrown if calculation of a cell's formula depends on the value of the same
8
- # cell.
9
- class RecursionError < StandardError
10
- end
11
-
12
- # Thrown if a referenced cell falls outside the limits of a supplied range.
13
- class RangeError < StandardError
14
- end
15
-
16
- # Thrown is access is attempted to an undefined cell.
17
- class UndefinedError < StandardError
18
- end
19
-
20
- end