From 3822fa4c7904900f1867e8d11371fa38b7241edd Mon Sep 17 00:00:00 2001 From: Tom Counsell Date: Sat, 29 Oct 2011 11:20:40 +0100 Subject: [PATCH] Tweaks to make sure that specs all pass --- .gitignore | 3 ++- lib/rubyfromexcel.rb | 2 +- rubyfromexcel.gemspec | 2 +- spec/array_formula_cell_spec.rb | 2 +- spec/arraying_formula_cell_spec.rb | 2 +- spec/dependency_builder_spec.rb | 14 +++++++++++++- spec/formula_builder_spec.rb | 13 +++++++++++++ spec/formula_peg_spec.rb | 1 + spec/shared_formula_cell_spec.rb | 2 +- spec/sharing_formula_cell_spec.rb | 10 +++++----- spec/simple_formula_cell_spec.rb | 10 +++++----- spec/value_cell_spec.rb | 16 ++++++++-------- spec/workbook_pruner_spec.rb | 7 ++++--- spec/workbook_spec.rb | 7 +++++-- spec/worksheet_spec.rb | 4 ++-- 15 files changed, 63 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index 9b15286..071cdf4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ _darcs/ examples/checkpoints/checkpoint/* examples/unzipped-sheets/* -examples/ruby-versions/* \ No newline at end of file +examples/ruby-versions/* +.DS_Store \ No newline at end of file diff --git a/lib/rubyfromexcel.rb b/lib/rubyfromexcel.rb index 623e7d1..d7984fd 100644 --- a/lib/rubyfromexcel.rb +++ b/lib/rubyfromexcel.rb @@ -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 diff --git a/rubyfromexcel.gemspec b/rubyfromexcel.gemspec index 5a0e819..1531d54 100644 --- a/rubyfromexcel.gemspec +++ b/rubyfromexcel.gemspec @@ -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" diff --git a/spec/array_formula_cell_spec.rb b/spec/array_formula_cell_spec.rb index 7ff9695..b44d05b 100644 --- a/spec/array_formula_cell_spec.rb +++ b/spec/array_formula_cell_spec.rb @@ -5,7 +5,7 @@ # B2:E2+A3:A62 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('7').root) + value_cell = ValueCell.new(mock('worksheet',:name => 'sheet1',:to_s => 'sheet1'),Nokogiri::XML('7').root) cell = ArrayFormulaCell.from_other_cell(value_cell) cell.array_formula_reference = "b3_array" diff --git a/spec/arraying_formula_cell_spec.rb b/spec/arraying_formula_cell_spec.rb index 09977f5..0e56de5 100644 --- a/spec/arraying_formula_cell_spec.rb +++ b/spec/arraying_formula_cell_spec.rb @@ -5,7 +5,7 @@ # B2:E2+A3:A62 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, diff --git a/spec/dependency_builder_spec.rb b/spec/dependency_builder_spec.rb index b4a9c74..554c607 100644 --- a/spec/dependency_builder_spec.rb +++ b/spec/dependency_builder_spec.rb @@ -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)) @@ -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 \ No newline at end of file diff --git a/spec/formula_builder_spec.rb b/spec/formula_builder_spec.rb index 9b6461c..cf812b7 100644 --- a/spec/formula_builder_spec.rb +++ b/spec/formula_builder_spec.rb @@ -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) diff --git a/spec/formula_peg_spec.rb b/spec/formula_peg_spec.rb index 3458d38..b1f1d78 100644 --- a/spec/formula_peg_spec.rb +++ b/spec/formula_peg_spec.rb @@ -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 diff --git a/spec/shared_formula_cell_spec.rb b/spec/shared_formula_cell_spec.rb index 95c47cc..9342b23 100644 --- a/spec/shared_formula_cell_spec.rb +++ b/spec/shared_formula_cell_spec.rb @@ -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('1').root ) @cell.shared_formula = Formula.parse("$A$1+B2") diff --git a/spec/sharing_formula_cell_spec.rb b/spec/sharing_formula_cell_spec.rb index 7b56cbd..7cf337b 100644 --- a/spec/sharing_formula_cell_spec.rb +++ b/spec/sharing_formula_cell_spec.rb @@ -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('A1+B21').root @@ -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('A1+B21').root @@ -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('A1+B21').root @@ -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('A1+B21').root @@ -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('A1+B21').root diff --git a/spec/simple_formula_cell_spec.rb b/spec/simple_formula_cell_spec.rb index 53aeb2e..36af9f7 100644 --- a/spec/simple_formula_cell_spec.rb +++ b/spec/simple_formula_cell_spec.rb @@ -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("1+23").root ) end @@ -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('AND(1,2)TRUE').root ) end @@ -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('"Hello "&A1Hello Bob').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 @@ -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("A1Hello Bob").root ) end diff --git a/spec/value_cell_spec.rb b/spec/value_cell_spec.rb index 995408f..1a3ad5c 100644 --- a/spec/value_cell_spec.rb +++ b/spec/value_cell_spec.rb @@ -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('24').root) + cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('24').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('TRUE').root) + cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('TRUE').root) cell.to_ruby.should == "def c22; true; end\n" - cell = ValueCell.new(mock('worksheet',:ruby_name => 'sheet1'),Nokogiri::XML('1').root) + cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('1').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('hello').root) + cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('hello').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('7').root) + cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('7').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('#N/A').root) + cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('#N/A').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('hello').root) + cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('hello').root) cell.can_be_replaced_with_value?.should be_true end @@ -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('7').root) + cell = ValueCell.new(mock('worksheet',:name => 'sheet1', :ruby_name => 'sheet1'),Nokogiri::XML('7').root) cell.work_out_dependencies cell.dependencies.should == [] end diff --git a/spec/workbook_pruner_spec.rb b/spec/workbook_pruner_spec.rb index 97f8998..c88b635 100644 --- a/spec/workbook_pruner_spec.rb +++ b/spec/workbook_pruner_spec.rb @@ -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 diff --git a/spec/workbook_spec.rb b/spec/workbook_spec.rb index 4fd7e5a..c488149 100644 --- a/spec/workbook_spec.rb +++ b/spec/workbook_spec.rb @@ -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 @@ -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 \ No newline at end of file diff --git a/spec/worksheet_spec.rb b/spec/worksheet_spec.rb index 4269fd3..4b1f716 100644 --- a/spec/worksheet_spec.rb +++ b/spec/worksheet_spec.rb @@ -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 @@ -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