soroban 0.7.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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