Skip to content

Commit

Permalink
Tweaks to make sure that specs all pass
Browse files Browse the repository at this point in the history
  • Loading branch information
tamc committed Oct 29, 2011
1 parent a4c1aec commit 3822fa4
Show file tree
Hide file tree
Showing 15 changed files with 63 additions and 32 deletions.
3 changes: 2 additions & 1 deletion .gitignore
@@ -1,4 +1,5 @@
_darcs/
examples/checkpoints/checkpoint/*
examples/unzipped-sheets/*
examples/ruby-versions/*
examples/ruby-versions/*
.DS_Store
2 changes: 1 addition & 1 deletion lib/rubyfromexcel.rb
Expand Up @@ -22,7 +22,7 @@ def self.debug(name,message)
return "" unless $DEBUG == true
file_name = "#{name.to_s}-debug.txt"
@files ||= {}
file = @files[file_name] ||= File.open(file_name.to_s,'w')
file = @files[file_name] ||= File.open(file_name,'w')
file.puts message
end

Expand Down
2 changes: 1 addition & 1 deletion rubyfromexcel.gemspec
Expand Up @@ -4,7 +4,7 @@ Gem::Specification.new do |s|
s.add_dependency('rubyscriptwriter','>= 0.0.1')
s.add_dependency('rubypeg','>= 0.0.2')
s.required_ruby_version = "~>1.9.1"
s.version = '0.0.13'
s.version = '0.0.14'
s.author = "Thomas Counsell, Green on Black Ltd"
s.email = "ruby-from-excel@greenonblack.com"
s.homepage = "http://github.com/tamc/rubyfromexcel"
Expand Down
2 changes: 1 addition & 1 deletion spec/array_formula_cell_spec.rb
Expand Up @@ -5,7 +5,7 @@
# <c r="B3"><f t="array" ref="B3:E6">B2:E2+A3:A6</f><v>2</v></c>

it "it is given a value cell and a pre-parsed formula and picks out values from its array references according to array_formula_offset" do
value_cell = ValueCell.new(mock('worksheet',:to_s => 'sheet1'),Nokogiri::XML('<c r="D6"><v>7</v></c>').root)
value_cell = ValueCell.new(mock('worksheet',:name => 'sheet1',:to_s => 'sheet1'),Nokogiri::XML('<c r="D6"><v>7</v></c>').root)

cell = ArrayFormulaCell.from_other_cell(value_cell)
cell.array_formula_reference = "b3_array"
Expand Down
2 changes: 1 addition & 1 deletion spec/arraying_formula_cell_spec.rb
Expand Up @@ -5,7 +5,7 @@
# <c r="B3"><f t="array" ref="B3:E6">B2:E2+A3:A6</f><v>2</v></c>

before do
@worksheet = mock(:worksheet,:class_name => 'Sheet1', :to_s => 'sheet1')
@worksheet = mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1', :to_s => 'sheet1')

@arraying_cell = ArrayingFormulaCell.new(
@worksheet,
Expand Down
14 changes: 13 additions & 1 deletion spec/dependency_builder_spec.rb
Expand Up @@ -74,6 +74,7 @@ def dependencies_for(formula)
@worksheet1.should_receive(:cell).with('c2').and_return(mock(:cell,:value_for_including => 'indirectvectors2',:can_be_replaced_with_value? => true))
dependencies_for('INDIRECT(C2&"["&C1&"]")').should == ["sheet1.c1","sheet1.c2","sheet2.a2", "sheet2.a3", "sheet2.a4", "sheet2.a5", "sheet2.a6", "sheet2.a7", "sheet2.a8", "sheet2.a9"]
end

it "should be able to deal with awkward combinations of tables and indirects" do
@worksheet1.should_receive(:cell).with('c102').twice.and_return(mock(:cell,:value_for_including => 'XVI.a',:can_be_replaced_with_value? => true))
@worksheet1.should_receive(:cell).with('a1').and_return(mock(:cell,:value_for_including => '2050',:can_be_replaced_with_value? => true))
Expand All @@ -84,5 +85,16 @@ def dependencies_for(formula)
dependencies_for(%q|INDIRECT("'"&XVI.a.Inputs[#Headers]&"'!Year.Matrix")|).should == ["sheet1.a20", "sheet1.a21", "sheet1.a22", "sheet1.c1"]
dependencies_for(%q|MATCH([Vector], INDIRECT("'"&XVI.a.Inputs[#Headers]&"'!Year.Matrix"), 0)|).should == ["sheet1.a20", "sheet1.a21", "sheet1.a22", "sheet1.c1", "sheet1.c30"]
end


it "Should be able to deal with a new combination of sumproduct, table and indirect that is causing a bug" do
workbook = mock(:workbook)
worksheet = mock(:worksheet,:name => "sheet13", :to_s => 'sheet13', :workbook => workbook)
workbook.stub!(:worksheets => {'sheet13' => worksheet})
worksheet.should_receive(:cell).with('d386').and_return(mock(:cell,:value_for_including => 'PM10',:can_be_replaced_with_value? => true))
worksheet.should_receive(:cell).with('g385').and_return(mock(:cell,:value_for_including => '2010',:can_be_replaced_with_value? => true))
cell = mock(:cell,:worksheet => worksheet, :reference => Reference.new('f386',worksheet))
@builder.formula_cell = cell
Table.new(worksheet,"EF.I.a.PM10","C90:O94",["Code", "Module", "Vector", "2007", "2010", "2015", "2020", "2025", "2030", "2035", "2040", "2045", "2050"],0)
dependencies_for('SUMPRODUCT(G$378:G$381,INDIRECT("EF.I.a."&$D386&"["&G$385&"]"))').should == ["sheet1.d386", "sheet1.g378", "sheet1.g379", "sheet1.g380", "sheet1.g381", "sheet1.g385", "sheet13.g91", "sheet13.g92", "sheet13.g93", "sheet13.g94"]
end
end
13 changes: 13 additions & 0 deletions spec/formula_builder_spec.rb
Expand Up @@ -129,6 +129,19 @@ def ruby_for(formula)
ruby_for('INDIRECT($C102&".Outputs[Vector]")').should == "sheet1.c30"
end

it "should convert a buggy case of table inside indirect" do
workbook = mock(:workbook)
worksheet = mock(:worksheet,:name => "sheet13", :to_s => 'sheet13', :workbook => workbook)
workbook.stub!(:worksheets => {'sheet13' => worksheet})
worksheet.should_receive(:cell).with('d386').and_return(mock(:cell,:value_for_including => 'PM10',:can_be_replaced_with_value? => true))
worksheet.should_receive(:cell).with('g385').and_return(mock(:cell,:value_for_including => '2010',:can_be_replaced_with_value? => true))
cell = mock(:cell,:worksheet => worksheet, :reference => Reference.new('f386',worksheet))
@builder.formula_cell = cell
Table.new(worksheet,"EF.I.a.PM10","C90:O94",["Code", "Module", "Vector", "2007", "2010", "2015", "2020", "2025", "2030", "2035", "2040", "2045", "2050"],0)

ruby_for('SUMPRODUCT(G$378:G$381,INDIRECT("EF.I.a."&$D386&"["&G$385&"]"))').should == "sumproduct(a('g378','g381'),sheet13.a('g91','g94'))"
end

it "should ignore external references, assuming that they point to an internal equivalent" do
formula_with_external = "INDEX([1]!Modules[Module], MATCH($C5, [1]!Modules[Code], 0))"
Table.new(mock(:worksheet,:to_s => 'sheet1',:name => "sheet1",),'Modules','a1:c41',['Module','Code','ColC'],1)
Expand Down
1 change: 1 addition & 0 deletions spec/formula_peg_spec.rb
Expand Up @@ -137,6 +137,7 @@ def check(text)
Formula.parse("INDEX(Global.Assumptions[Households], MATCH(F$321,Global.Assumptions[Year], 0))").to_ast.should == [:formula, [:function, "INDEX", [:table_reference, "Global.Assumptions", "Households"], [:function, "MATCH", [:cell, "F$321"], [:table_reference, "Global.Assumptions", "Year"], [:number, "0"]]]]
Formula.parse("MAX(-SUM(I.a.Inputs[2007])-F$80,0)").to_ast.should == [:formula, [:function, "MAX", [:arithmetic, [:prefix, "-", [:function, "SUM", [:table_reference, "I.a.Inputs", "2007"]]], [:operator, "-"], [:cell, "F$80"]], [:number, "0"]]]
Formula.parse('DeptSales_101[Sale Amount]').to_ast.should == [:formula,[:table_reference,'DeptSales_101','Sale Amount']]
Formula.parse('EF.I.a.PM10[2010]').to_ast.should == [:formula,[:table_reference,'EF.I.a.PM10','2010']]
end

it "returns booleans" do
Expand Down
2 changes: 1 addition & 1 deletion spec/shared_formula_cell_spec.rb
Expand Up @@ -4,7 +4,7 @@

before do
@cell = SharedFormulaCell.new(
mock(:worksheet,:class_name => 'Sheet1', :to_s => 'sheet1'),
mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1', :to_s => 'sheet1'),
Nokogiri::XML('<c r="W8" s="49"><f t="shared" si="2"/><v>1</v></c>').root
)
@cell.shared_formula = Formula.parse("$A$1+B2")
Expand Down
10 changes: 5 additions & 5 deletions spec/sharing_formula_cell_spec.rb
Expand Up @@ -2,7 +2,7 @@

describe SharingFormulaCell do
it "it is given a pre-parsed formula and offsets its references by the amount of its shared_formula_offset" do
worksheet = mock(:worksheet,:class_name => 'Sheet1',:to_s => 'sheet1')
worksheet = mock(:worksheet,:name => 'sheet1',:class_name => 'Sheet1',:to_s => 'sheet1')
sharing_cell = SharingFormulaCell.new(
worksheet,
Nokogiri::XML('<c r="W7" s="49"><f t="shared" ref="W7:W8" si="2">A1+B2</f><v>1</v></c>').root
Expand All @@ -19,7 +19,7 @@
end

it "knows what it depends upon" do
worksheet = mock(:worksheet,:class_name => 'Sheet1',:to_s => 'sheet1')
worksheet = mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1',:to_s => 'sheet1')
sharing_cell = SharingFormulaCell.new(
worksheet,
Nokogiri::XML('<c r="W7" s="49"><f t="shared" ref="W7:W8" si="2">A1+B2</f><v>1</v></c>').root
Expand All @@ -38,7 +38,7 @@
end

it "it should cope if it is sharing only with itself" do
worksheet = mock(:worksheet,:class_name => 'Sheet1')
worksheet = mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1',:to_s => 'sheet1')
sharing_cell = SharingFormulaCell.new(
worksheet,
Nokogiri::XML('<c r="W7" s="49"><f t="shared" ref="W7" si="2">A1+B2</f><v>1</v></c>').root
Expand All @@ -49,7 +49,7 @@
end

it "it should cope if it is sharing to a formula that doesn't exist" do
worksheet = mock(:worksheet,:class_name => 'Sheet1')
worksheet = mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1',:to_s => 'sheet1')
sharing_cell = SharingFormulaCell.new(
worksheet,
Nokogiri::XML('<c r="W7" s="49"><f t="shared" ref="W7:W8" si="2">A1+B2</f><v>1</v></c>').root
Expand All @@ -61,7 +61,7 @@
end

it "it should cope if it is sharing with a cell that has its own formula" do
worksheet = mock(:worksheet,:class_name => 'Sheet1')
worksheet = mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1',:to_s => 'sheet1')
sharing_cell = SharingFormulaCell.new(
worksheet,
Nokogiri::XML('<c r="W7" s="49"><f t="shared" ref="W7:W8" si="2">A1+B2</f><v>1</v></c>').root
Expand Down
10 changes: 5 additions & 5 deletions spec/simple_formula_cell_spec.rb
Expand Up @@ -4,7 +4,7 @@

before do
@cell = SimpleFormulaCell.new(
mock(:worksheet,:class_name => 'Sheet1', :to_s => 'sheet1'),
mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1', :to_s => 'sheet1'),
Nokogiri::XML("<c r=\"C3\"><f>1+2</f><v>3</v></c>").root
)
end
Expand All @@ -27,7 +27,7 @@

before do
@cell = SimpleFormulaCell.new(
mock(:worksheet,:class_name => 'Sheet1', :to_s => 'sheet1'),
mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1', :to_s => 'sheet1'),
Nokogiri::XML('<c r="C3" t="b"><f>AND(1,2)</f><v>TRUE</v></c>').root
)
end
Expand All @@ -46,13 +46,13 @@

before do
@cell = SimpleFormulaCell.new(
mock(:worksheet,:class_name => 'Sheet1', :to_s => 'sheet1'),
mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1', :to_s => 'sheet1'),
Nokogiri::XML('<c r="C3" t="str"><f>"Hello "&amp;A1</f><v>Hello Bob</v></c>').root
)
end

it "should use the FormulaPeg to create ruby code for a formula and turn that into a method" do
@cell.to_ruby.should == %Q{def c3; @c3 ||= "Hello "+a1.to_s; end\n}
@cell.to_ruby.should == %Q{def c3; @c3 ||= "Hello "+(a1).to_s; end\n}
end

it "should create a test for the ruby code" do
Expand All @@ -65,7 +65,7 @@

before do
@cell = SimpleFormulaCell.new(
mock(:worksheet,:class_name => 'Sheet1', :to_s => 'sheet1'),
mock(:worksheet,:name => 'sheet1', :class_name => 'Sheet1', :to_s => 'sheet1'),
Nokogiri::XML("<c r=\"C3\" t=\"str\"><f>A1</f><v>Hello Bob</v></c>").root
)
end
Expand Down
16 changes: 8 additions & 8 deletions spec/value_cell_spec.rb
Expand Up @@ -3,36 +3,36 @@
describe ValueCell do

it "creates a ruby method that returns the cell's value as a float if it is a number" do
cell = ValueCell.new(mock('worksheet',:ruby_name => 'sheet1'),Nokogiri::XML('<c r="C22"><v>24</v></c>').root)
cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('<c r="C22"><v>24</v></c>').root)
cell.to_ruby.should == "def c22; 24.0; end\n"
end

it "creates a ruby method that returns the cell's value as a ruby boolean if it is a boolean" do
cell = ValueCell.new(mock('worksheet',:ruby_name => 'sheet1'),Nokogiri::XML('<c r="C22" t="b"><v>TRUE</v></c>').root)
cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('<c r="C22" t="b"><v>TRUE</v></c>').root)
cell.to_ruby.should == "def c22; true; end\n"
cell = ValueCell.new(mock('worksheet',:ruby_name => 'sheet1'),Nokogiri::XML('<c r="C22" t="b"><v>1</v></c>').root)
cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('<c r="C22" t="b"><v>1</v></c>').root)
cell.to_ruby.should == "def c22; true; end\n"

end

it "creates a ruby method that returns the cell's value as a ruby string if it is a string" do
cell = ValueCell.new(mock('worksheet',:ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="str"><v>hello</v></c>').root)
cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="str"><v>hello</v></c>').root)
cell.to_ruby.should == %Q{def b6; "hello"; end\n}
end

it "creates a ruby method that returns the cell's value as a ruby string if it is a shared string" do
cell = ValueCell.new(mock('worksheet',:ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="s"><v>7</v></c>').root)
cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="s"><v>7</v></c>').root)
SharedStrings.should_receive(:shared_string_for).with(7).and_return('hello')
cell.to_ruby.should == %Q{def b6; "hello"; end\n}
end

it "creates a ruby method that returns an appropriate symbol if it is an error" do
cell = ValueCell.new(mock('worksheet',:ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="e"><v>#N/A</v></c>').root)
cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="e"><v>#N/A</v></c>').root)
cell.to_ruby.should == "def b6; :na; end\n"
end

it "has a method that confirms it can be replaced with its value" do
cell = ValueCell.new(mock('worksheet',:ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="str"><v>hello</v></c>').root)
cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="str"><v>hello</v></c>').root)
cell.can_be_replaced_with_value?.should be_true
end

Expand All @@ -41,7 +41,7 @@
describe ValueCell, "can list the cells upon which it depends" do

it "in simple cases knows what it depends upon" do
cell = ValueCell.new(mock('worksheet',:ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="s"><v>7</v></c>').root)
cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('<c r="B6" t="s"><v>7</v></c>').root)
cell.work_out_dependencies
cell.dependencies.should == []
end
Expand Down
7 changes: 4 additions & 3 deletions spec/workbook_pruner_spec.rb
Expand Up @@ -10,17 +10,18 @@
it "should be able prune any cells not required" do
workbook = mock(:workbook)
workbook.should_receive(:work_out_dependencies)
sheet1 = mock(:worksheet)
sheet1 = mock(:worksheet,:name =>'sheet1')
workbook.should_receive(:worksheets).at_least(:once).and_return({'sheet1' => sheet1})
workbook.should_receive(:total_cells).and_return(2)
cell1 = mock(:cell)
cell1 = mock(:cell,:worksheet => sheet1,:reference => 'c1')
sheet1.should_receive(:cells).at_least(:once).and_return({'a1' => cell1})
cell1.should_receive(:dependencies).and_return(['sheet1.a2'])
cell2 = mock(:cell)
cell2 = mock(:cell,:worksheet => sheet1,:reference => 'c2')
workbook.should_receive(:cell).with('sheet1.a2').and_return(cell2)
cell2.should_receive(:dependencies).and_return([])
SheetNames.instance.clear
SheetNames.instance['Output Sheet'] = 'sheet1'
workbook.work_out_dependencies
wb = WorkbookPruner.new(workbook)
wb.prune_cells_not_needed_for_output_sheets('Output Sheet')
end
Expand Down
7 changes: 5 additions & 2 deletions spec/workbook_spec.rb
Expand Up @@ -75,7 +75,7 @@
# coding: utf-8
# First sheet
class Sheet1 < Spreadsheet
def a1; @a1 ||= excel_if(a2=="Hello","hello",sheet2.b4); end
def a1; @a1 ||= excel_if(excel_comparison(a2,"==","Hello"),"hello",sheet2.b4); end
def a2; "Hello"; end
end
Expand Down Expand Up @@ -270,14 +270,17 @@ def c9; @c9 ||= sheet3.a3; end
end

it "should prune any cells that aren't needed for the output sheet calculations when output sheets have been specified" do
@workbook.work_out_dependencies
@workbook.prune_cells_not_needed_for_output_sheets('Outputs')
@workbook.worksheets['sheet2'].to_ruby.should == pruning_calc_sheet_ruby_output_prune
end

it "should convert cells to values where they don't depend on inputs, and then prune" do
@workbook.prune_cells_not_needed_for_output_sheets('Outputs')
@workbook.work_out_dependencies
@workbook.convert_cells_to_values_when_independent_of_input_sheets('Inputs')
@workbook.prune_cells_not_needed_for_output_sheets('Outputs')
@workbook.worksheets['sheet2'].to_ruby.should == pruning_calc_sheet_ruby_input_and_output_prune
$DEBUG = false
end

end
4 changes: 2 additions & 2 deletions spec/worksheet_spec.rb
Expand Up @@ -11,7 +11,7 @@
# coding: utf-8
# Outputs
class Sheet1 < Spreadsheet
def a1; @a1 ||= excel_if(a2=="Hello","hello",sheet2.b4); end
def a1; @a1 ||= excel_if(excel_comparison(a2,"==","Hello"),"hello",sheet2.b4); end
def a2; "A shared string"; end
end
Expand Down Expand Up @@ -46,7 +46,7 @@ def sheet1; $spreadsheet ||= Spreadsheet.new; $spreadsheet.sheet1; end
# coding: utf-8
# Outputs
class Sheet1 < Spreadsheet
def a1; @a1 ||= excel_if(a2=="Hello","hello",sheet2.b4); end
def a1; @a1 ||= excel_if(excel_comparison(a2,"==","Hello"),"hello",sheet2.b4); end
def a2; "A shared string"; end
def reference_one; sheet2.a1; end
end
Expand Down

0 comments on commit 3822fa4

Please sign in to comment.