soroban 0.5.4 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/Gemfile.lock +6 -4
- data/README.md +9 -1
- data/Soroban.gemspec +7 -3
- data/VERSION +1 -1
- data/lib/soroban/cell.rb +15 -6
- data/lib/soroban/parser/nodes.rb +46 -10
- data/lib/soroban/parser/rewrite.rb +35 -5
- data/lib/soroban/sheet.rb +56 -0
- data/lib/soroban/tabulator.rb +34 -0
- data/spec/import_spec.rb +41 -0
- data/spec/soroban_spec.rb +6 -0
- metadata +33 -18
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
GEM
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
|
+
awesome_print (1.1.0)
|
4
5
|
diff-lcs (1.1.3)
|
5
6
|
git (1.2.5)
|
6
7
|
jeweler (1.8.4)
|
@@ -8,11 +9,11 @@ GEM
|
|
8
9
|
git (>= 1.2.5)
|
9
10
|
rake
|
10
11
|
rdoc
|
11
|
-
json (1.7.
|
12
|
+
json (1.7.7)
|
12
13
|
nokogiri (1.5.6)
|
13
14
|
polyglot (0.3.3)
|
14
15
|
rake (10.0.3)
|
15
|
-
rdoc (3.12)
|
16
|
+
rdoc (3.12.2)
|
16
17
|
json (~> 1.4)
|
17
18
|
redcarpet (2.2.2)
|
18
19
|
rspec (2.12.0)
|
@@ -22,18 +23,19 @@ GEM
|
|
22
23
|
rspec-core (2.12.2)
|
23
24
|
rspec-expectations (2.12.1)
|
24
25
|
diff-lcs (~> 1.1.3)
|
25
|
-
rspec-mocks (2.12.
|
26
|
+
rspec-mocks (2.12.2)
|
26
27
|
rubyXL (1.2.10)
|
27
28
|
rubyzip (0.9.9)
|
28
29
|
treetop (1.4.12)
|
29
30
|
polyglot
|
30
31
|
polyglot (>= 0.3.1)
|
31
|
-
yard (0.8.
|
32
|
+
yard (0.8.5.2)
|
32
33
|
|
33
34
|
PLATFORMS
|
34
35
|
ruby
|
35
36
|
|
36
37
|
DEPENDENCIES
|
38
|
+
awesome_print
|
37
39
|
jeweler (~> 1.8.3)
|
38
40
|
nokogiri (>= 1.4.4)
|
39
41
|
rake
|
data/README.md
CHANGED
@@ -3,8 +3,9 @@ Soroban
|
|
3
3
|
|
4
4
|
Soroban is a calculating engine that understands Excel formulas.
|
5
5
|
|
6
|
-
[![
|
6
|
+
[![Code Climate](https://codeclimate.com/github/agworld/soroban.png)](https://codeclimate.com/github/agworld/soroban)
|
7
7
|
[![Dependency Status](https://gemnasium.com/agworld/soroban.png)](https://gemnasium.com/agworld/soroban)
|
8
|
+
[![Build Status](https://secure.travis-ci.org/agworld/soroban.png)](http://travis-ci.org/#!/agworld/soroban)
|
8
9
|
|
9
10
|
|
10
11
|
Getting Started
|
@@ -146,6 +147,13 @@ s.g = "=FOO(10, 20)"
|
|
146
147
|
puts s.g # => 17
|
147
148
|
```
|
148
149
|
|
150
|
+
Compilation
|
151
|
+
-----------
|
152
|
+
|
153
|
+
Rather than interact with a `Soroban::Sheet` object at runtime, you can compile
|
154
|
+
the sheet into a Ruby or JavaScript class which you can then either save out to
|
155
|
+
a file or evaluate directly. This is slightly less flexible, but more efficient.
|
156
|
+
|
149
157
|
Contributing to Soroban
|
150
158
|
-----------------------
|
151
159
|
|
data/Soroban.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "soroban"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.7.2"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jason Hutchens"]
|
12
|
-
s.date = "2013-
|
12
|
+
s.date = "2013-09-03"
|
13
13
|
s.description = "Soroban makes it easy to extract and execute formulas from Excel spreadsheets. It rewrites Excel formulas as Ruby expressions, and allows you to bind named variables to spreadsheet cells to easily manipulate inputs and capture outputs."
|
14
14
|
s.email = "jason.hutchens@agworld.com.au"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -55,6 +55,7 @@ Gem::Specification.new do |s|
|
|
55
55
|
"lib/soroban/parser/nodes.rb",
|
56
56
|
"lib/soroban/parser/rewrite.rb",
|
57
57
|
"lib/soroban/sheet.rb",
|
58
|
+
"lib/soroban/tabulator.rb",
|
58
59
|
"lib/soroban/value_walker.rb",
|
59
60
|
"spec/documentation_spec.rb",
|
60
61
|
"spec/import_spec.rb",
|
@@ -64,7 +65,7 @@ Gem::Specification.new do |s|
|
|
64
65
|
s.homepage = "https://github.com/agworld/soroban"
|
65
66
|
s.licenses = ["MIT"]
|
66
67
|
s.require_paths = ["lib"]
|
67
|
-
s.rubygems_version = "1.8.
|
68
|
+
s.rubygems_version = "1.8.25"
|
68
69
|
s.summary = "Soroban is a calculating engine that understands Excel formulas."
|
69
70
|
|
70
71
|
if s.respond_to? :specification_version then
|
@@ -75,17 +76,20 @@ Gem::Specification.new do |s|
|
|
75
76
|
s.add_development_dependency(%q<rubyXL>, ["~> 1.2.7"])
|
76
77
|
s.add_development_dependency(%q<nokogiri>, [">= 1.4.4"])
|
77
78
|
s.add_development_dependency(%q<rubyzip>, [">= 0.9.4"])
|
79
|
+
s.add_development_dependency(%q<awesome_print>, [">= 0"])
|
78
80
|
else
|
79
81
|
s.add_dependency(%q<treetop>, ["~> 1.4.10"])
|
80
82
|
s.add_dependency(%q<rubyXL>, ["~> 1.2.7"])
|
81
83
|
s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
|
82
84
|
s.add_dependency(%q<rubyzip>, [">= 0.9.4"])
|
85
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
83
86
|
end
|
84
87
|
else
|
85
88
|
s.add_dependency(%q<treetop>, ["~> 1.4.10"])
|
86
89
|
s.add_dependency(%q<rubyXL>, ["~> 1.2.7"])
|
87
90
|
s.add_dependency(%q<nokogiri>, [">= 1.4.4"])
|
88
91
|
s.add_dependency(%q<rubyzip>, [">= 0.9.4"])
|
92
|
+
s.add_dependency(%q<awesome_print>, [">= 0"])
|
89
93
|
end
|
90
94
|
end
|
91
95
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.2
|
data/lib/soroban/cell.rb
CHANGED
@@ -7,7 +7,7 @@ module Soroban
|
|
7
7
|
# representation of its contents, and the executable Ruby version of same, as
|
8
8
|
# generated via a rewrite grammar. Cells also store their dependencies.
|
9
9
|
class Cell
|
10
|
-
attr_reader :excel, :
|
10
|
+
attr_reader :excel, :javascript, :dependencies
|
11
11
|
|
12
12
|
# Cells are initialised with a binding to allow formulas to be executed
|
13
13
|
# within the context of the sheet which owns the cell.
|
@@ -18,12 +18,19 @@ module Soroban
|
|
18
18
|
@value = nil
|
19
19
|
end
|
20
20
|
|
21
|
+
def to_compiled_ruby
|
22
|
+
@tree.to_compiled_ruby
|
23
|
+
end
|
24
|
+
|
21
25
|
# Set the contents of a cell, and store the executable Ruby version.
|
22
26
|
def set(contents)
|
23
27
|
contents = contents.to_s
|
24
28
|
contents = "'#{contents}'" if Soroban::unknown?(contents)
|
25
29
|
clear
|
26
|
-
@excel
|
30
|
+
@excel = contents
|
31
|
+
@tree = Soroban::parser.parse(@excel)
|
32
|
+
raise Soroban::ParseError, Soroban::parser.failure_reason if @tree.nil?
|
33
|
+
@ruby = _to_ruby
|
27
34
|
end
|
28
35
|
|
29
36
|
# Clear the cached value of a cell to force it to be recalculated
|
@@ -45,10 +52,12 @@ module Soroban
|
|
45
52
|
|
46
53
|
private
|
47
54
|
|
48
|
-
def
|
49
|
-
tree
|
50
|
-
|
51
|
-
|
55
|
+
def _to_ruby
|
56
|
+
@tree.to_ruby(@dependencies.clear)
|
57
|
+
end
|
58
|
+
|
59
|
+
def _to_javascript
|
60
|
+
@tree.to_javascript(@dependencies.clear)
|
52
61
|
end
|
53
62
|
|
54
63
|
end
|
data/lib/soroban/parser/nodes.rb
CHANGED
@@ -1,67 +1,103 @@
|
|
1
1
|
module Soroban
|
2
2
|
|
3
3
|
class Formula < Treetop::Runtime::SyntaxNode
|
4
|
-
def
|
4
|
+
def rewrite_ruby(value)
|
5
5
|
value.gsub(/^= */, '')
|
6
6
|
end
|
7
|
+
alias :compile_ruby :rewrite_ruby
|
7
8
|
end
|
8
9
|
|
9
10
|
class Identifier < Treetop::Runtime::SyntaxNode
|
10
|
-
def
|
11
|
+
def rewrite_ruby(value)
|
11
12
|
"@#{value}.get"
|
12
13
|
end
|
14
|
+
def compile_ruby(value)
|
15
|
+
"@cells[:#{value}].call"
|
16
|
+
end
|
13
17
|
def extract(value)
|
14
18
|
value.to_sym
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
18
22
|
class IntegerValue < Treetop::Runtime::SyntaxNode
|
19
|
-
def
|
23
|
+
def rewrite_ruby(value)
|
20
24
|
"#{value.to_f}"
|
21
25
|
end
|
26
|
+
alias :compile_ruby :rewrite_ruby
|
22
27
|
end
|
23
28
|
|
24
29
|
class FloatValue < Treetop::Runtime::SyntaxNode
|
25
|
-
def
|
30
|
+
def rewrite_ruby(value)
|
26
31
|
"#{value.to_f}"
|
27
32
|
end
|
33
|
+
alias :compile_ruby :rewrite_ruby
|
28
34
|
end
|
29
35
|
|
30
36
|
class Function < Treetop::Runtime::SyntaxNode
|
31
|
-
def
|
37
|
+
def rewrite_ruby(value)
|
32
38
|
match = /^([^(]*)(.*)$/.match(value)
|
33
39
|
"func_#{match[1].downcase}#{match[2]}"
|
34
40
|
end
|
41
|
+
def compile_ruby(value)
|
42
|
+
match = /^([A-Z]+)\((.*)\)$/.match(value)
|
43
|
+
name, args = match[1], match[2].split(',')
|
44
|
+
case name
|
45
|
+
when 'VLOOKUP'
|
46
|
+
find, table, column, _ = args
|
47
|
+
table = table[1...-1]
|
48
|
+
column = column.to_i
|
49
|
+
table_key = "'#{table}_#{column}'"
|
50
|
+
code = []
|
51
|
+
code << "begin"
|
52
|
+
code << " @cache[#{table_key}] ||= {"
|
53
|
+
cols = Tabulator.new(table).get
|
54
|
+
lookup = Hash[cols[0].zip(cols[column-1])]
|
55
|
+
code << lookup.map do |key, val|
|
56
|
+
" @cells[:#{key}].call => @cells[:#{val}].call"
|
57
|
+
end.join(",\n")
|
58
|
+
code << " }"
|
59
|
+
code << " @cache[#{table_key}][#{find}] || 0.0"
|
60
|
+
code << " end"
|
61
|
+
code.join("\n")
|
62
|
+
else
|
63
|
+
value
|
64
|
+
end
|
65
|
+
end
|
35
66
|
end
|
36
67
|
|
37
68
|
class Pow < Treetop::Runtime::SyntaxNode
|
38
|
-
def
|
69
|
+
def rewrite_ruby(value)
|
39
70
|
"**"
|
40
71
|
end
|
72
|
+
alias :compile_ruby :rewrite_ruby
|
41
73
|
end
|
42
74
|
|
43
75
|
class Equal < Treetop::Runtime::SyntaxNode
|
44
|
-
def
|
76
|
+
def rewrite_ruby(value)
|
45
77
|
"=="
|
46
78
|
end
|
79
|
+
alias :compile_ruby :rewrite_ruby
|
47
80
|
end
|
48
81
|
|
49
82
|
class NotEqual < Treetop::Runtime::SyntaxNode
|
50
|
-
def
|
83
|
+
def rewrite_ruby(value)
|
51
84
|
"!="
|
52
85
|
end
|
86
|
+
alias :compile_ruby :rewrite_ruby
|
53
87
|
end
|
54
88
|
|
55
89
|
class Label < Treetop::Runtime::SyntaxNode
|
56
|
-
def
|
90
|
+
def rewrite_ruby(value)
|
57
91
|
value.gsub('$', '')
|
58
92
|
end
|
93
|
+
alias :compile_ruby :rewrite_ruby
|
59
94
|
end
|
60
95
|
|
61
96
|
class Range < Treetop::Runtime::SyntaxNode
|
62
|
-
def
|
97
|
+
def rewrite_ruby(value)
|
63
98
|
"'#{value}'"
|
64
99
|
end
|
100
|
+
alias :compile_ruby :rewrite_ruby
|
65
101
|
def extract(value)
|
66
102
|
LabelWalker.new(value).map { |label| "#{label}".to_sym }
|
67
103
|
end
|
@@ -2,19 +2,49 @@ module Treetop
|
|
2
2
|
module Runtime
|
3
3
|
class SyntaxNode
|
4
4
|
|
5
|
-
def
|
5
|
+
def to_ruby(dependencies)
|
6
6
|
if nonterminal?
|
7
7
|
value = ""
|
8
|
-
elements.each { |element| value << element.
|
8
|
+
elements.each { |element| value << element.to_ruby(dependencies) }
|
9
9
|
_add_dependency(dependencies, extract(value))
|
10
|
-
|
10
|
+
rewrite_ruby(value)
|
11
11
|
else
|
12
12
|
_add_dependency(dependencies, extract(text_value))
|
13
|
-
|
13
|
+
rewrite_ruby(text_value)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
17
|
+
def to_compiled_ruby
|
18
|
+
if nonterminal?
|
19
|
+
value = ""
|
20
|
+
elements.each { |element| value << element.to_compiled_ruby }
|
21
|
+
compile_ruby(value)
|
22
|
+
else
|
23
|
+
compile_ruby(text_value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_javascript(dependencies)
|
28
|
+
if nonterminal?
|
29
|
+
value = ""
|
30
|
+
elements.each { |element| value << element.to_javascript(dependencies) }
|
31
|
+
_add_dependency(dependencies, extract(value))
|
32
|
+
rewrite_javascript(value)
|
33
|
+
else
|
34
|
+
_add_dependency(dependencies, extract(text_value))
|
35
|
+
rewrite_javascript(text_value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def compile_ruby(value)
|
40
|
+
value
|
41
|
+
end
|
42
|
+
|
43
|
+
def rewrite_ruby(value)
|
44
|
+
value
|
45
|
+
end
|
46
|
+
|
47
|
+
def rewrite_javascript(value)
|
18
48
|
value
|
19
49
|
end
|
20
50
|
|
data/lib/soroban/sheet.rb
CHANGED
@@ -2,8 +2,11 @@ require 'soroban/helpers'
|
|
2
2
|
require 'soroban/functions'
|
3
3
|
require 'soroban/label_walker'
|
4
4
|
require 'soroban/value_walker'
|
5
|
+
require 'soroban/tabulator'
|
5
6
|
require 'soroban/cell'
|
6
7
|
|
8
|
+
require 'set'
|
9
|
+
|
7
10
|
module Soroban
|
8
11
|
|
9
12
|
# A container for cells.
|
@@ -14,10 +17,62 @@ module Soroban
|
|
14
17
|
def initialize(logger=nil)
|
15
18
|
@logger = logger
|
16
19
|
@cells = {}
|
20
|
+
@compiled = {}
|
17
21
|
@changes = Hash.new{ |h, k| h[k] = Set.new }
|
18
22
|
@bindings = {}
|
19
23
|
end
|
20
24
|
|
25
|
+
def factory(name)
|
26
|
+
eval(self.to_ruby(name), TOPLEVEL_BINDING)
|
27
|
+
Object::const_get('Soroban').const_get('Model').const_get(name).new
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return a string containing a ruby class that implements the sheet. You can
|
31
|
+
# call eval() on this string to create the class, which you can then
|
32
|
+
# instantiate. Set inputs on the instance and read outputs off.
|
33
|
+
def to_ruby(class_name)
|
34
|
+
data = []
|
35
|
+
data << "module Soroban"
|
36
|
+
data << "module Model"
|
37
|
+
data << "class #{class_name}"
|
38
|
+
data << " def initialize"
|
39
|
+
data << " @binds = {"
|
40
|
+
data << bindings.map do |name, cell|
|
41
|
+
" '#{name}' => :#{cell}"
|
42
|
+
end.join(",\n")
|
43
|
+
data << " }"
|
44
|
+
data << " @cache = {}"
|
45
|
+
data << " @cells = {"
|
46
|
+
data << @compiled.map do |label, cell|
|
47
|
+
" :#{label} => lambda { @cache[:#{label}] ||= #{cell.to_compiled_ruby} }"
|
48
|
+
end.join(",\n")
|
49
|
+
data << " }"
|
50
|
+
data << " end"
|
51
|
+
data << " def clear"
|
52
|
+
data << " @cache.clear"
|
53
|
+
data << " end"
|
54
|
+
data << " def get(name)"
|
55
|
+
data << " @cells[@binds[name]].call"
|
56
|
+
data << " end"
|
57
|
+
data << " def set(name, value)"
|
58
|
+
data << " self.clear"
|
59
|
+
data << " @cells[@binds[name]] = lambda { @cache[@binds[name]] ||= value }"
|
60
|
+
data << " end"
|
61
|
+
bindings.each do |name, cell|
|
62
|
+
data << " def #{name}"
|
63
|
+
data << " get('#{name}')"
|
64
|
+
data << " end"
|
65
|
+
data << " def #{name}=(value)"
|
66
|
+
data << " set('#{name}', value)"
|
67
|
+
data << " end"
|
68
|
+
end
|
69
|
+
data << "end"
|
70
|
+
data << "end"
|
71
|
+
data << "end"
|
72
|
+
puts data.join("\n")
|
73
|
+
data.join("\n")
|
74
|
+
end
|
75
|
+
|
21
76
|
# Used for calling dynamically defined functions, and for creating new
|
22
77
|
# cells (via `label=`).
|
23
78
|
def method_missing(method, *args, &block)
|
@@ -125,6 +180,7 @@ module Soroban
|
|
125
180
|
internal = "@#{label}"
|
126
181
|
_expose(internal, label)
|
127
182
|
cell = Cell.new(binding)
|
183
|
+
@compiled[label] = cell
|
128
184
|
_set(label, cell, contents)
|
129
185
|
instance_variable_set(internal, cell)
|
130
186
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Soroban
|
2
|
+
|
3
|
+
# An enumerable that splits a range of cells into an nxm array
|
4
|
+
class Tabulator
|
5
|
+
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
# Create a new walker from a supplied range.
|
9
|
+
def initialize(range)
|
10
|
+
@fc, @fr, @tc, @tr = Soroban::getRange(range)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get
|
14
|
+
row = []
|
15
|
+
cols = [row]
|
16
|
+
col_ref, row_ref = @fc, @fr
|
17
|
+
while true do
|
18
|
+
row << "#{col_ref}#{row_ref}".to_sym
|
19
|
+
break if row_ref == @tr && col_ref == @tc
|
20
|
+
if row_ref == @tr
|
21
|
+
row = []
|
22
|
+
cols << row
|
23
|
+
row_ref = @fr
|
24
|
+
col_ref = col_ref.next
|
25
|
+
else
|
26
|
+
row_ref = row_ref.next
|
27
|
+
end
|
28
|
+
end
|
29
|
+
cols
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
data/spec/import_spec.rb
CHANGED
@@ -28,6 +28,47 @@ describe "Documentation", :if => has_rubyxl do
|
|
28
28
|
puts s.force # => 710.044826106394
|
29
29
|
s.force.should be_within(0.01).of(710.04)
|
30
30
|
|
31
|
+
require 'benchmark'
|
32
|
+
|
33
|
+
i_time = Benchmark.realtime do
|
34
|
+
1000.times do
|
35
|
+
s.planet = 'Earth'
|
36
|
+
s.mass = 80
|
37
|
+
s.force
|
38
|
+
s.planet = 'Venus'
|
39
|
+
s.mass = 80
|
40
|
+
s.force
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
puts "Interpreted Time: #{i_time}"
|
45
|
+
|
46
|
+
eval(s.to_ruby("Test"))
|
47
|
+
model = Soroban::Model::Test.new
|
48
|
+
|
49
|
+
model.planet = 'Earth'
|
50
|
+
model.mass = 80
|
51
|
+
model.force.should be_within(0.01).of(783.46)
|
52
|
+
|
53
|
+
model.planet = 'Venus'
|
54
|
+
model.mass = 80
|
55
|
+
model.force.should be_within(0.01).of(710.04)
|
56
|
+
|
57
|
+
c_time = Benchmark.realtime do
|
58
|
+
1000.times do
|
59
|
+
model.planet = 'Earth'
|
60
|
+
model.mass = 80
|
61
|
+
model.force
|
62
|
+
model.planet = 'Venus'
|
63
|
+
model.mass = 80
|
64
|
+
model.force
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
puts "Compiled Time: #{c_time}"
|
69
|
+
|
70
|
+
(10.0 * c_time).should be < i_time
|
71
|
+
|
31
72
|
end
|
32
73
|
|
33
74
|
end
|
data/spec/soroban_spec.rb
CHANGED
@@ -61,6 +61,12 @@ describe "Soroban" do
|
|
61
61
|
sheet.bindings.keys.should include :output
|
62
62
|
sheet.bindings.values.should include :A1
|
63
63
|
sheet.bindings.values.should include :A2
|
64
|
+
|
65
|
+
model = sheet.factory('Test')
|
66
|
+
model.input = 5
|
67
|
+
model.output.should eq(25)
|
68
|
+
model.input = 4
|
69
|
+
model.output.should eq(16)
|
64
70
|
end
|
65
71
|
|
66
72
|
it "can bind variables to ranges" do
|
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:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 7
|
9
|
+
- 2
|
10
|
+
version: 0.7.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jason Hutchens
|
@@ -15,11 +15,10 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2013-
|
18
|
+
date: 2013-09-03 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
|
22
|
-
version_requirements: &id001 !ruby/object:Gem::Requirement
|
21
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
22
|
none: false
|
24
23
|
requirements:
|
25
24
|
- - ~>
|
@@ -31,11 +30,11 @@ dependencies:
|
|
31
30
|
- 10
|
32
31
|
version: 1.4.10
|
33
32
|
type: :runtime
|
34
|
-
|
33
|
+
version_requirements: *id001
|
34
|
+
name: treetop
|
35
35
|
prerelease: false
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
|
-
|
38
|
-
version_requirements: &id002 !ruby/object:Gem::Requirement
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
38
|
none: false
|
40
39
|
requirements:
|
41
40
|
- - ~>
|
@@ -47,11 +46,11 @@ dependencies:
|
|
47
46
|
- 7
|
48
47
|
version: 1.2.7
|
49
48
|
type: :development
|
50
|
-
|
49
|
+
version_requirements: *id002
|
50
|
+
name: rubyXL
|
51
51
|
prerelease: false
|
52
52
|
- !ruby/object:Gem::Dependency
|
53
|
-
|
54
|
-
version_requirements: &id003 !ruby/object:Gem::Requirement
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
54
|
none: false
|
56
55
|
requirements:
|
57
56
|
- - ">="
|
@@ -63,11 +62,11 @@ dependencies:
|
|
63
62
|
- 4
|
64
63
|
version: 1.4.4
|
65
64
|
type: :development
|
66
|
-
|
65
|
+
version_requirements: *id003
|
66
|
+
name: nokogiri
|
67
67
|
prerelease: false
|
68
68
|
- !ruby/object:Gem::Dependency
|
69
|
-
|
70
|
-
version_requirements: &id004 !ruby/object:Gem::Requirement
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
70
|
none: false
|
72
71
|
requirements:
|
73
72
|
- - ">="
|
@@ -79,7 +78,22 @@ dependencies:
|
|
79
78
|
- 4
|
80
79
|
version: 0.9.4
|
81
80
|
type: :development
|
82
|
-
|
81
|
+
version_requirements: *id004
|
82
|
+
name: rubyzip
|
83
|
+
prerelease: false
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
type: :development
|
95
|
+
version_requirements: *id005
|
96
|
+
name: awesome_print
|
83
97
|
prerelease: false
|
84
98
|
description: Soroban makes it easy to extract and execute formulas from Excel spreadsheets. It rewrites Excel formulas as Ruby expressions, and allows you to bind named variables to spreadsheet cells to easily manipulate inputs and capture outputs.
|
85
99
|
email: jason.hutchens@agworld.com.au
|
@@ -129,6 +143,7 @@ files:
|
|
129
143
|
- lib/soroban/parser/nodes.rb
|
130
144
|
- lib/soroban/parser/rewrite.rb
|
131
145
|
- lib/soroban/sheet.rb
|
146
|
+
- lib/soroban/tabulator.rb
|
132
147
|
- lib/soroban/value_walker.rb
|
133
148
|
- spec/documentation_spec.rb
|
134
149
|
- spec/import_spec.rb
|
@@ -163,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
178
|
requirements: []
|
164
179
|
|
165
180
|
rubyforge_project:
|
166
|
-
rubygems_version: 1.8.
|
181
|
+
rubygems_version: 1.8.25
|
167
182
|
signing_key:
|
168
183
|
specification_version: 3
|
169
184
|
summary: Soroban is a calculating engine that understands Excel formulas.
|