From 0f688c0d983e43abb25b8fffcf0b324eaf041642 Mon Sep 17 00:00:00 2001 From: Paul Denya Date: Fri, 29 Jan 2016 15:13:28 -0800 Subject: [PATCH 01/61] fixed an issue with col_name causing it to fail generating names for single letter columns --- lib/creek/sheet.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index 780569a..d7aec26 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -49,7 +49,7 @@ def rows_with_meta_data # Returns valid Excel column name for a given column index. # For example, returns "A" for 0, "B" for 1 and "AQ" for 42. def col_name i - quot = i/26 + quot = (i/26).floor (quot>0 ? col_name(quot-1) : "") + (i%26+65).chr end From bfedfa4197d37e174ab95af94b630ba7e0a69983 Mon Sep 17 00:00:00 2001 From: Ilyas Garaev Date: Thu, 14 Apr 2016 12:33:13 +0300 Subject: [PATCH 02/61] refactor Creek::Sheet class --- lib/creek/sheet.rb | 37 +++++++++---------------------------- 1 file changed, 9 insertions(+), 28 deletions(-) diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index 780569a..d0f80b2 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -21,13 +21,6 @@ def initialize book, name, sheetid, state, visible, rid, sheetfile @rid = rid @state = state @sheetfile = sheetfile - - # An XLS file has only 256 columns, however, an XLSX or XLSM file can contain up to 16384 columns. - # This function creates a hash with all valid XLSX column names and associated indices. - @excel_col_names = Hash.new - (0...16384).each do |i| - @excel_col_names[col_name(i)] = i - end end ## @@ -45,13 +38,6 @@ def rows_with_meta_data end private - ## - # Returns valid Excel column name for a given column index. - # For example, returns "A" for 0, "B" for 1 and "AQ" for 42. - def col_name i - quot = i/26 - (quot>0 ? col_name(quot-1) : "") + (i%26+65).chr - end ## # Returns a hash per row that includes the cell ids and values. @@ -64,7 +50,7 @@ def rows_generator include_meta_data=false opener = Nokogiri::XML::Reader::TYPE_ELEMENT closer = Nokogiri::XML::Reader::TYPE_END_ELEMENT Enumerator.new do |y| - shared, row, cells, cell = false, nil, {}, nil + row, cells, cell = nil, {}, nil cell_type = nil cell_style_idx = nil @book.files.file.open(path) do |xml| @@ -82,9 +68,8 @@ def rows_generator include_meta_data=false cell_type = node.attributes['t'] cell_style_idx = node.attributes['s'] cell = node.attributes['r'] - elsif (node.name.eql? 'v') and (node.node_type.eql? opener) - if !cell.nil? + unless cell.nil? cells[cell] = convert(node.inner_xml, cell_type, cell_style_idx) end end @@ -106,22 +91,18 @@ def converter_options ## # The unzipped XML file does not contain any node for empty cells. # Empty cells are being padded in using this function - def fill_in_empty_cells cells, row_number, last_col + def fill_in_empty_cells(cells, row_number, last_col) new_cells = Hash.new + unless cells.empty? - keys = cells.keys.sort last_col = last_col.gsub(row_number, '') - last_col_index = @excel_col_names[last_col] - [*(0..last_col_index)].each do |i| - col = col_name i - id = "#{col}#{row_number}" - unless cells.has_key? id - new_cells[id] = nil - else - new_cells[id] = cells[id] - end + + ("A"..last_col).to_a.each do |column| + id = "#{column}#{row_number}" + new_cells[id] = cells[id] end end + new_cells end end From 4c52f054e950ba6d911928ba7b409831a648ccbc Mon Sep 17 00:00:00 2001 From: Kamil Hismatullin Date: Thu, 21 Apr 2016 11:56:56 +0300 Subject: [PATCH 03/61] get date attribute from workbookPr and use it during dates conversion --- lib/creek/book.rb | 21 +++++++++++++++++++++ lib/creek/sheet.rb | 5 ++++- lib/creek/styles/converter.rb | 15 --------------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/creek/book.rb b/lib/creek/book.rb index 1501689..8734dd2 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -37,5 +37,26 @@ def style_types def close @files.close end + + def base_date + @base_date ||= + begin + # Default to 1900 (minus one day due to excel quirk) but use 1904 if + # it's set in the Workbook's workbookPr + # http://msdn.microsoft.com/en-us/library/ff530155(v=office.12).aspx + result = Date.new(1899, 12, 30) # default + + doc = @files.file.open "xl/workbook.xml" + xml = Nokogiri::XML::Document.parse doc + xml.css('workbookPr[date1904]').each do |workbookPr| + if workbookPr['date1904'] =~ /true|1/i + result = Date.new(1904, 1, 1) + break + end + end + + result + end + end end end diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index 780569a..821b18a 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -100,7 +100,10 @@ def convert(value, type, style_idx) end def converter_options - @converter_options ||= {shared_strings: @book.shared_strings.dictionary} + @converter_options ||= { + shared_strings: @book.shared_strings.dictionary, + base_date: @book.base_date + } end ## diff --git a/lib/creek/styles/converter.rb b/lib/creek/styles/converter.rb index 59f6714..8c7bb1c 100644 --- a/lib/creek/styles/converter.rb +++ b/lib/creek/styles/converter.rb @@ -98,21 +98,6 @@ def self.convert_bignum(value) value.to_f end end - - ## Returns the base_date from which to calculate dates. - # Defaults to 1900 (minus two days due to excel quirk), but use 1904 if - # it's set in the Workbook's workbookPr. - # http://msdn.microsoft.com/en-us/library/ff530155(v=office.12).aspx - def base_date - @base_date ||= begin - # return DATE_SYSTEM_1900 if xml.workbook == nil - # xml.workbook.xpath("//workbook/workbookPr[@date1904]").each do |workbookPr| - # return DATE_SYSTEM_1904 if workbookPr["date1904"] =~ /true|1/i - # end - DATE_SYSTEM_1900 - end - end - end end end From 1f5063cb2fd7cdfc2f3f665472475ab27619ba1a Mon Sep 17 00:00:00 2001 From: Kamil Hismatullin Date: Thu, 21 Apr 2016 14:34:00 +0300 Subject: [PATCH 04/61] extract dates constants to book class and freeze them --- lib/creek/book.rb | 8 ++++++-- lib/creek/styles/constants.rb | 5 ----- lib/creek/styles/converter.rb | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/creek/book.rb b/lib/creek/book.rb index 8734dd2..d613ea6 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -1,5 +1,6 @@ require 'zip/filesystem' require 'nokogiri' +require 'date' module Creek @@ -9,6 +10,9 @@ class Creek::Book :sheets, :shared_strings + DATE_1900 = Date.new(1899, 12, 30).freeze + DATE_1904 = Date.new(1904, 1, 1).freeze + def initialize path, options = {} check_file_extension = options.fetch(:check_file_extension, true) if check_file_extension @@ -44,13 +48,13 @@ def base_date # Default to 1900 (minus one day due to excel quirk) but use 1904 if # it's set in the Workbook's workbookPr # http://msdn.microsoft.com/en-us/library/ff530155(v=office.12).aspx - result = Date.new(1899, 12, 30) # default + result = DATE_1900 # default doc = @files.file.open "xl/workbook.xml" xml = Nokogiri::XML::Document.parse doc xml.css('workbookPr[date1904]').each do |workbookPr| if workbookPr['date1904'] =~ /true|1/i - result = Date.new(1904, 1, 1) + result = DATE_1904 break end end diff --git a/lib/creek/styles/constants.rb b/lib/creek/styles/constants.rb index 849c0ba..4cde485 100644 --- a/lib/creek/styles/constants.rb +++ b/lib/creek/styles/constants.rb @@ -1,5 +1,3 @@ -require 'date' - module Creek class Styles module Constants @@ -38,9 +36,6 @@ module Constants 48 => :bignum, # ##0.0E+0 49 => :unsupported # @ } - - DATE_SYSTEM_1900 = Date.new(1899, 12, 30) - DATE_SYSTEM_1904 = Date.new(1904, 1, 1) end end end diff --git a/lib/creek/styles/converter.rb b/lib/creek/styles/converter.rb index 8c7bb1c..a94cd52 100644 --- a/lib/creek/styles/converter.rb +++ b/lib/creek/styles/converter.rb @@ -81,7 +81,7 @@ def self.convert_date(value, options) fraction_of_24 = value - days_since_date_system_start # http://stackoverflow.com/questions/10559767/how-to-convert-ms-excel-date-from-float-to-date-format-in-ruby - date = options.fetch(:base_date, DATE_SYSTEM_1900) + days_since_date_system_start + date = options.fetch(:base_date, Date.new(1899, 12, 30)) + days_since_date_system_start if fraction_of_24 > 0 # there is a time associated seconds = (fraction_of_24 * 86400).round From 7e5e6f4dc2f968b6a30cd4731c9ce26a8ae2ee98 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Thu, 21 Apr 2016 14:43:16 -0400 Subject: [PATCH 05/61] PR ##26 merged. --- lib/creek/sheet.rb | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index d7aec26..967a3e2 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -21,13 +21,6 @@ def initialize book, name, sheetid, state, visible, rid, sheetfile @rid = rid @state = state @sheetfile = sheetfile - - # An XLS file has only 256 columns, however, an XLSX or XLSM file can contain up to 16384 columns. - # This function creates a hash with all valid XLSX column names and associated indices. - @excel_col_names = Hash.new - (0...16384).each do |i| - @excel_col_names[col_name(i)] = i - end end ## @@ -45,13 +38,6 @@ def rows_with_meta_data end private - ## - # Returns valid Excel column name for a given column index. - # For example, returns "A" for 0, "B" for 1 and "AQ" for 42. - def col_name i - quot = (i/26).floor - (quot>0 ? col_name(quot-1) : "") + (i%26+65).chr - end ## # Returns a hash per row that includes the cell ids and values. @@ -64,7 +50,7 @@ def rows_generator include_meta_data=false opener = Nokogiri::XML::Reader::TYPE_ELEMENT closer = Nokogiri::XML::Reader::TYPE_END_ELEMENT Enumerator.new do |y| - shared, row, cells, cell = false, nil, {}, nil + row, cells, cell = nil, {}, nil cell_type = nil cell_style_idx = nil @book.files.file.open(path) do |xml| @@ -82,9 +68,8 @@ def rows_generator include_meta_data=false cell_type = node.attributes['t'] cell_style_idx = node.attributes['s'] cell = node.attributes['r'] - elsif (node.name.eql? 'v') and (node.node_type.eql? opener) - if !cell.nil? + unless cell.nil? cells[cell] = convert(node.inner_xml, cell_type, cell_style_idx) end end @@ -106,23 +91,19 @@ def converter_options ## # The unzipped XML file does not contain any node for empty cells. # Empty cells are being padded in using this function - def fill_in_empty_cells cells, row_number, last_col + def fill_in_empty_cells(cells, row_number, last_col) new_cells = Hash.new + unless cells.empty? - keys = cells.keys.sort last_col = last_col.gsub(row_number, '') - last_col_index = @excel_col_names[last_col] - [*(0..last_col_index)].each do |i| - col = col_name i - id = "#{col}#{row_number}" - unless cells.has_key? id - new_cells[id] = nil - else - new_cells[id] = cells[id] - end + + ("A"..last_col).to_a.each do |column| + id = "#{column}#{row_number}" + new_cells[id] = cells[id] end end + new_cells end end -end +end \ No newline at end of file From 545e30334339cf8cc27150d2d3e6fcdc261558de Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Thu, 21 Apr 2016 14:54:10 -0400 Subject: [PATCH 06/61] chore: Bump up the gem version to 1.1.2 --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index ffaf445..0065afd 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "1.1.1" + VERSION = "1.1.2" end From e361d6fe25dc01064cefcbccfcb48d7ec7def12b Mon Sep 17 00:00:00 2001 From: Maciej Majewski Date: Sun, 1 Jan 2017 03:39:28 +0100 Subject: [PATCH 07/61] Fix converting values formatted as percentage (#33) Values formatted as percentage values should not be divided by 100. They are already stored as decimals (0.15 for 15%). --- lib/creek/styles/converter.rb | 4 +--- spec/fixtures/sample.xlsx | Bin 8954 -> 9189 bytes spec/test_spec.rb | 6 ++++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/creek/styles/converter.rb b/lib/creek/styles/converter.rb index a94cd52..01a54e6 100644 --- a/lib/creek/styles/converter.rb +++ b/lib/creek/styles/converter.rb @@ -58,10 +58,8 @@ def self.call(value, type, style, options = {}) value when :fixnum value.to_i - when :float + when :float, :percentage value.to_f - when :percentage - value.to_f / 100 when :date, :time, :date_time convert_date(value, options) when :bignum diff --git a/spec/fixtures/sample.xlsx b/spec/fixtures/sample.xlsx index 58f6dd3e554ae1db9541ed261ed1727cce45ccbc..8310529ff695c4b43570c2305b13de264f56d785 100644 GIT binary patch delta 6019 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 delta 5912 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 diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 5baea2f..5a5026c 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -32,7 +32,8 @@ {'A4'=>'Content 7', 'B4'=>'Content 8', 'C4'=>'Content 9', 'D4'=>'Content 10', 'E4'=>'Content 11', 'F4'=>'Content 12'}, {'A5'=>nil, 'B5'=>nil, 'C5'=>nil, 'D5'=>nil, 'E5'=>nil, 'F5'=>nil, 'G5'=>nil, 'H5'=>nil, 'I5'=>nil, 'J5'=>nil, 'K5'=>nil, 'L5'=>nil, 'M5'=>nil, 'N5'=>nil, 'O5'=>nil, 'P5'=>nil, 'Q5'=>nil, 'R5'=>nil, 'S5'=>nil, 'T5'=>nil, 'U5'=>nil, 'V5'=>nil, 'W5'=>nil, 'X5'=>nil, 'Y5'=>nil, 'Z5'=>'Z Content', 'AA5'=>nil, 'AB5'=>nil, 'AC5'=>nil, 'AD5'=>nil, 'AE5'=>nil, 'AF5'=>nil, 'AG5'=>nil, 'AH5'=>nil, 'AI5'=>nil, 'AJ5'=>nil, 'AK5'=>nil, 'AL5'=>nil, 'AM5'=>nil, 'AN5'=>nil, 'AO5'=>nil, 'AP5'=>nil, 'AQ5'=>nil, 'AR5'=>nil, 'AS5'=>nil, 'AT5'=>nil, 'AU5'=>nil, 'AV5'=>nil, 'AW5'=>nil, 'AX5'=>nil, 'AY5'=>nil, 'AZ5'=>'Content 13'}, {'A6'=>'1', 'B6'=>'2', 'C6'=>'3'}, {'A7'=>'Content 15', 'B7'=>'Content 16', 'C7'=>'Content 18', 'D7'=>'Content 19'}, - {'A8'=>nil, 'B8'=>'Content 20', 'C8'=>nil, 'D8'=>nil, 'E8'=>nil, 'F8'=>'Content 21'}] + {'A8'=>nil, 'B8'=>'Content 20', 'C8'=>nil, 'D8'=>nil, 'E8'=>nil, 'F8'=>'Content 21'}, + {'A10' => 0.15, 'B10' => 0.15}] end after(:all) do @@ -67,7 +68,8 @@ rows[5].should == @expected_rows[5] rows[6].should == @expected_rows[6] rows[7].should == @expected_rows[7] - row_count.should == 8 + rows[8].should == @expected_rows[8] + row_count.should == 9 end it 'Parse rows with empty cells and meta data successfully.' do From 5c6c12117992edd82be5bf2023af8ca461f6b986 Mon Sep 17 00:00:00 2001 From: Melita Kokot Date: Sun, 1 Jan 2017 03:40:04 +0100 Subject: [PATCH 08/61] Parsing images (#32) * Add image parsing support * Refactor image parsing logic and fix some issues * Add comments to new methods for image parsing in sheet * Add tests for parsing images * Refactor image parsing, add comments and update tests * Update Readme --- README.rdoc | 21 +++++ lib/creek.rb | 4 +- lib/creek/drawing.rb | 109 ++++++++++++++++++++++++++ lib/creek/sheet.rb | 50 +++++++++++- lib/creek/utils.rb | 16 ++++ spec/drawing_spec.rb | 52 ++++++++++++ spec/fixtures/sample-with-images.xlsx | Bin 0 -> 21923 bytes spec/sheet_spec.rb | 85 ++++++++++++++++++++ 8 files changed, 335 insertions(+), 2 deletions(-) create mode 100644 lib/creek/drawing.rb create mode 100644 lib/creek/utils.rb create mode 100644 spec/drawing_spec.rb create mode 100644 spec/fixtures/sample-with-images.xlsx create mode 100644 spec/sheet_spec.rb diff --git a/README.rdoc b/README.rdoc index cbdedd8..670fab3 100644 --- a/README.rdoc +++ b/README.rdoc @@ -51,6 +51,27 @@ Creek can parse this directly without the need for file upload gems such as Carr Creek::Book.new file.path, check_file_extension: false end +== Parsing images +Creek does not parse images by default. If you want to parse the images, +use *with_images* method before iterating over rows to preload images information. If you don't call this method, Creek will not return images anywhere. + + +Cells with images will be an array of Pathname objects. +If an image is spread across multiple cells, same Pathname object will be returned for each cell. + + sheet.with_images.rows.each do |row| + puts row # => {"A1"=>[#], "B2"=>"Fluffy"} + end + + +Images for a specific cell can be obtained with images_at method: + puts sheet.images_at('A1') # => [#] + + # no images in a cell + puts sheet.images_at('C1') # => nil + +Creek will most likely return nil for a cell with images if there is no other text cell in that row - you can use *images_at* method for retrieving images in that cell. + == Contributing Contributions are welcomed. You can fork a repository, add your code changes to the forked branch, ensure all existing unit tests pass, create new unit tests cover your new changes and finally create a pull request. diff --git a/lib/creek.rb b/lib/creek.rb index 490106c..47201b9 100644 --- a/lib/creek.rb +++ b/lib/creek.rb @@ -1,9 +1,11 @@ -require "creek/version" +require 'creek/version' require 'creek/book' require 'creek/styles/constants' require 'creek/styles/style_types' require 'creek/styles/converter' +require 'creek/utils' require 'creek/styles' +require 'creek/drawing' require 'creek/sheet' require 'creek/shared_strings' diff --git a/lib/creek/drawing.rb b/lib/creek/drawing.rb new file mode 100644 index 0000000..f018652 --- /dev/null +++ b/lib/creek/drawing.rb @@ -0,0 +1,109 @@ +require 'pathname' + +module Creek + class Creek::Drawing + include Creek::Utils + + COLUMNS = ('A'..'AZ').to_a + + def initialize(book, drawing_filepath) + @book = book + @drawing_filepath = drawing_filepath + @drawings = [] + @drawings_rels = [] + @images_pathnames = Hash.new { |hash, key| hash[key] = [] } + + if file_exist?(@drawing_filepath) + load_drawings_and_rels + load_images_pathnames_by_cells if has_images? + end + end + + ## + # Returns false if there are no images in the drawing file or the drawing file does not exist, true otherwise. + def has_images? + @has_images ||= !@drawings.empty? + end + + ## + # Extracts images from excel to tmpdir for a cell, if the images are not already extracted (multiple calls or same image file in multiple cells). + # Returns array of images as Pathname objects or nil. + def images_at(cell_name) + coordinate = calc_coordinate(cell_name) + pathnames_at_coordinate = @images_pathnames[coordinate] + return if pathnames_at_coordinate.empty? + + pathnames_at_coordinate.map do |image_pathname| + if image_pathname.exist? + image_pathname + else + excel_image_path = "xl/media/#{image_pathname.to_path.split(tmpdir).last}" + IO.copy_stream(@book.files.file.open(excel_image_path), image_pathname.to_path) + image_pathname + end + end + end + + private + + ## + # Transforms cell name to [row, col], e.g. A1 => [0, 0], B3 => [1, 2] + # Rows and cols start with 0. + def calc_coordinate(cell_name) + col = COLUMNS.index(cell_name.slice /[A-Z]+/) + row = (cell_name.slice /\d+/).to_i - 1 # rows in drawings start with 0 + [row, col] + end + + ## + # Creates/loads temporary directory for extracting images from excel + def tmpdir + @tmpdir ||= ::Dir.mktmpdir('creek__drawing') + end + + ## + # Parses drawing and drawing's relationships xmls. + # Drawing xml contains relationships ID's and coordinates (row, col). + # Drawing relationships xml contains images' locations. + def load_drawings_and_rels + @drawings = parse_xml(@drawing_filepath).css('xdr|twoCellAnchor') + drawing_rels_filepath = expand_to_rels_path(@drawing_filepath) + @drawings_rels = parse_xml(drawing_rels_filepath).css('Relationships') + end + + ## + # Iterates through the drawings and saves images' paths as Pathname objects to a hash with [row, col] keys. + # As multiple images can be located in a single cell, hash values are array of Pathname objects. + # One image can be spread across multiple cells (defined with from-row/to-row/from-col/to-col attributes) - same Pathname object is associated to each row-col combination for the range. + def load_images_pathnames_by_cells + image_selector = 'xdr:pic/xdr:blipFill/a:blip'.freeze + row_from_selector = 'xdr:from/xdr:row'.freeze + row_to_selector = 'xdr:to/xdr:row'.freeze + col_from_selector = 'xdr:from/xdr:col'.freeze + col_to_selector = 'xdr:to/xdr:col'.freeze + + @drawings.xpath('//xdr:twoCellAnchor').each do |drawing| + embed = drawing.xpath(image_selector).first.attributes['embed'] + next if embed.nil? + + rid = embed.value + path = Pathname.new("#{tmpdir}#{extract_drawing_path(rid).slice(/[^\/]*$/)}") + + row_from = drawing.xpath(row_from_selector).text.to_i + col_from = drawing.xpath(col_from_selector).text.to_i + row_to = drawing.xpath(row_to_selector).text.to_i + col_to = drawing.xpath(col_to_selector).text.to_i + + (col_from..col_to).each do |col| + (row_from..row_to).each do |row| + @images_pathnames[[row, col]].push(path) + end + end + end + end + + def extract_drawing_path(rid) + @drawings_rels.css("Relationship[@Id=#{rid}]").first.attributes['Target'].value + end + end +end \ No newline at end of file diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index ca450b9..b83a14d 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -3,6 +3,7 @@ module Creek class Creek::Sheet + include Creek::Utils attr_reader :book, :name, @@ -21,6 +22,28 @@ def initialize book, name, sheetid, state, visible, rid, sheetfile @rid = rid @state = state @sheetfile = sheetfile + @images_present = false + end + + ## + # Preloads images info (coordinates and paths) from related drawing.xml and drawing rels. + # Must be called before #rows method if you want to have images included. + # Returns self so you can chain the calls (sheet.with_images.rows). + def with_images + @drawingfile = extract_drawing_filepath + if @drawingfile + @drawing = Creek::Drawing.new(@book, @drawingfile.sub('..', 'xl')) + @images_present = @drawing.has_images? + end + self + end + + ## + # Extracts images for a cell to a temporary folder. + # Returns array of Pathnames for the cell. + # Returns nil if images asre not found for the cell or images were not preloaded with #with_images. + def images_at(cell) + @drawing.images_at(cell) if @images_present end ## @@ -62,6 +85,14 @@ def rows_generator include_meta_data=false y << (include_meta_data ? row : cells) if node.self_closing? elsif (node.name.eql? 'row') and (node.node_type.eql? closer) processed_cells = fill_in_empty_cells(cells, row['r'], cell) + + if @images_present + processed_cells.each do |cell_name, cell_value| + next unless cell_value.nil? + processed_cells[cell_name] = images_at(cell_name) + end + end + row['cells'] = processed_cells y << (include_meta_data ? row : processed_cells) elsif (node.name.eql? 'c') and (node.node_type.eql? opener) @@ -108,5 +139,22 @@ def fill_in_empty_cells(cells, row_number, last_col) new_cells end + + ## + # Find drawing filepath for the current sheet. + # Sheet xml contains drawing relationship ID. + # Sheet relationships xml contains drawing file's location. + def extract_drawing_filepath + # Read drawing relationship ID from the sheet. + sheet_filepath = "xl/#{@sheetfile}" + drawing = parse_xml(sheet_filepath).css('drawing').first + return if drawing.nil? + + drawing_rid = drawing.attributes['id'].value + + # Read sheet rels to find drawing file's location. + sheet_rels_filepath = expand_to_rels_path(sheet_filepath) + parse_xml(sheet_rels_filepath).css("Relationship[@Id='#{drawing_rid}']").first.attributes['Target'].value + end end -end \ No newline at end of file +end diff --git a/lib/creek/utils.rb b/lib/creek/utils.rb new file mode 100644 index 0000000..9eef47c --- /dev/null +++ b/lib/creek/utils.rb @@ -0,0 +1,16 @@ +module Creek + module Utils + def expand_to_rels_path(filepath) + filepath.sub(/(\/[^\/]+$)/, '/_rels\1.rels') + end + + def file_exist?(path) + @book.files.file.exist?(path) + end + + def parse_xml(xml_path) + doc = @book.files.file.open(xml_path) + Nokogiri::XML::Document.parse(doc) + end + end +end diff --git a/spec/drawing_spec.rb b/spec/drawing_spec.rb new file mode 100644 index 0000000..632e01b --- /dev/null +++ b/spec/drawing_spec.rb @@ -0,0 +1,52 @@ +require './spec/spec_helper' + +describe 'drawing' do + let(:book) { Creek::Book.new('spec/fixtures/sample-with-images.xlsx') } + let(:book_no_images) { Creek::Book.new('spec/fixtures/sample.xlsx') } + let(:drawingfile) { 'xl/drawings/drawing1.xml' } + let(:drawing) { Creek::Drawing.new(book, drawingfile) } + let(:drawing_without_images) { Creek::Drawing.new(book_no_images, drawingfile) } + + describe '#has_images?' do + it 'has' do + expect(drawing.has_images?).to eq(true) + end + + it 'does not have' do + expect(drawing_without_images.has_images?).to eq(false) + end + end + + describe '#images_at' do + it 'returns images pathnames at cell' do + image = drawing.images_at('A2')[0] + expect(image.class).to eq(Pathname) + expect(image.exist?).to eq(true) + expect(image.to_path).to match(/.+creek__drawing.+\.jpeg$/) + end + + context 'when no images in cell' do + it 'returns nil' do + images = drawing.images_at('B2') + expect(images).to eq(nil) + end + end + + context 'when more images in one cell' do + it 'returns all images at cell' do + images = drawing.images_at('A10') + expect(images.size).to eq(2) + expect(images.all?(&:exist?)).to eq(true) + end + end + + context 'when same image across multiple cells' do + it 'returns same image for each cell' do + image1 = drawing.images_at('A4')[0] + image2 = drawing.images_at('A5')[0] + expect(image1.class).to eq(Pathname) + expect(image1).to eq(image2) + end + end + end +end diff --git a/spec/fixtures/sample-with-images.xlsx b/spec/fixtures/sample-with-images.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d3706e4931541f16625a5b7f311670b1e4996deb GIT binary patch literal 21923 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 Date: Thu, 5 Jan 2017 18:49:36 +0200 Subject: [PATCH 09/61] add ability to fetch file from remote (#34) --- README.rdoc | 5 +++++ lib/creek/book.rb | 2 ++ spec/test_spec.rb | 8 ++++++++ 3 files changed, 15 insertions(+) diff --git a/README.rdoc b/README.rdoc index 670fab3..76cd364 100644 --- a/README.rdoc +++ b/README.rdoc @@ -72,6 +72,11 @@ Images for a specific cell can be obtained with images_at method: Creek will most likely return nil for a cell with images if there is no other text cell in that row - you can use *images_at* method for retrieving images in that cell. +== Remote files + + remote_url = 'http://dev-builds.libreoffice.org/tmp/test.xlsx' + Creek::Book.new remote_url, remote: true + == Contributing Contributions are welcomed. You can fork a repository, add your code changes to the forked branch, ensure all existing unit tests pass, create new unit tests cover your new changes and finally create a pull request. diff --git a/lib/creek/book.rb b/lib/creek/book.rb index d613ea6..3783aa3 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -1,6 +1,7 @@ require 'zip/filesystem' require 'nokogiri' require 'date' +require 'open-uri' module Creek @@ -19,6 +20,7 @@ def initialize path, options = {} extension = File.extname(options[:original_filename] || path).downcase raise 'Not a valid file format.' unless (['.xlsx', '.xlsm'].include? extension) end + path = open(path) if options[:remote] @files = Zip::File.open path @shared_strings = SharedStrings.new(self) end diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 5a5026c..eec100d 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -15,6 +15,14 @@ open_book.should raise_error 'Not a valid file format.' end + it 'Fail to open remote file' do + lambda { Creek::Book.new 'http://dev-builds.libreoffice.org/tmp/test.xlsx' }.should raise_error + end + + it 'Opens remote file with remote flag' do + lambda { Creek::Book.new 'http://dev-builds.libreoffice.org/tmp/test.xlsx', remote: true }.should_not raise_error + end + it 'Check file extension of original_filename if passed.' do path = 'spec/fixtures/temp_string_io_file_path_with_no_extension' lambda { Creek::Book.new path, :original_filename => 'invalid.xls' }.should raise_error 'Not a valid file format.' From 75078dd0ac50b49a110506d907d2dd40741db854 Mon Sep 17 00:00:00 2001 From: Mikhail Nikalyukin Date: Tue, 14 Feb 2017 20:45:45 +0200 Subject: [PATCH 10/61] more robust way to fetch remote file, httparty dependency added. (#35) --- creek.gemspec | 1 + lib/creek/book.rb | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/creek.gemspec b/creek.gemspec index 1b3c6c8..8532792 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -27,4 +27,5 @@ Gem::Specification.new do |spec| spec.add_dependency 'nokogiri', '~> 1.6.0' spec.add_dependency 'rubyzip', '>= 1.0.0' + spec.add_dependency 'httparty', '~> 0.14.0' end diff --git a/lib/creek/book.rb b/lib/creek/book.rb index 3783aa3..558ed81 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -1,7 +1,7 @@ require 'zip/filesystem' require 'nokogiri' require 'date' -require 'open-uri' +require 'httparty' module Creek @@ -20,8 +20,14 @@ def initialize path, options = {} extension = File.extname(options[:original_filename] || path).downcase raise 'Not a valid file format.' unless (['.xlsx', '.xlsm'].include? extension) end - path = open(path) if options[:remote] - @files = Zip::File.open path + if options[:remote] + zipfile = Tempfile.new("file") + zipfile.binmode + zipfile.write(HTTParty.get(path).body) + zipfile.close + path = zipfile.path + end + @files = Zip::File.open(path) @shared_strings = SharedStrings.new(self) end From 2e9425ef5da1595982ec93fa03956db7b0f32775 Mon Sep 17 00:00:00 2001 From: Melita Kokot Date: Wed, 12 Apr 2017 20:13:10 +0200 Subject: [PATCH 11/61] Fix for temp image path (#39) * Fix tmp image path * Fix specs for excel without images --- lib/creek/drawing.rb | 16 ++++++++-------- spec/sheet_spec.rb | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/creek/drawing.rb b/lib/creek/drawing.rb index f018652..f695979 100644 --- a/lib/creek/drawing.rb +++ b/lib/creek/drawing.rb @@ -34,12 +34,12 @@ def images_at(cell_name) return if pathnames_at_coordinate.empty? pathnames_at_coordinate.map do |image_pathname| - if image_pathname.exist? - image_pathname - else - excel_image_path = "xl/media/#{image_pathname.to_path.split(tmpdir).last}" - IO.copy_stream(@book.files.file.open(excel_image_path), image_pathname.to_path) - image_pathname + if image_pathname.exist? + image_pathname + else + excel_image_path = "xl/media#{image_pathname.to_path.split(tmpdir).last}" + IO.copy_stream(@book.files.file.open(excel_image_path), image_pathname.to_path) + image_pathname end end end @@ -87,7 +87,7 @@ def load_images_pathnames_by_cells next if embed.nil? rid = embed.value - path = Pathname.new("#{tmpdir}#{extract_drawing_path(rid).slice(/[^\/]*$/)}") + path = Pathname.new("#{tmpdir}/#{extract_drawing_path(rid).slice(/[^\/]*$/)}") row_from = drawing.xpath(row_from_selector).text.to_i col_from = drawing.xpath(col_from_selector).text.to_i @@ -106,4 +106,4 @@ def extract_drawing_path(rid) @drawings_rels.css("Relationship[@Id=#{rid}]").first.attributes['Target'].value end end -end \ No newline at end of file +end diff --git a/spec/sheet_spec.rb b/spec/sheet_spec.rb index d537d00..887109e 100644 --- a/spec/sheet_spec.rb +++ b/spec/sheet_spec.rb @@ -56,7 +56,7 @@ def load_cell(rows, cell_name) context 'with excel without images' do it 'does not break on with_images' do rows = sheet_no_images.with_images.rows.map { |r| r } - expect(load_cell(rows, 'A10')).to eq(nil) + expect(load_cell(rows, 'A10')).to eq(0.15) end end end From d9b3df7ecb0ad53112a872590193e4a38bff0dba Mon Sep 17 00:00:00 2001 From: Melita Kokot Date: Wed, 12 Apr 2017 20:13:33 +0200 Subject: [PATCH 12/61] Change Readme to markdown, add syntax highlighting (#40) I changed the Readme format to markdown, fixed some typos and added syntax highlighting. --- README.md | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.rdoc | 102 --------------------------------------------- 2 files changed, 117 insertions(+), 102 deletions(-) create mode 100644 README.md delete mode 100644 README.rdoc diff --git a/README.md b/README.md new file mode 100644 index 0000000..a06b396 --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ +# Creek - Stream parser for large Excel (xlsx and xlsm) files. + +Creek is a Ruby gem that provides a fast, simple and efficient method of parsing large Excel (xlsx and xlsm) files. + + +## Installation + +Creek can be used from the command line or as part of a Ruby web framework. To install the gem using terminal, run the following command: + +``` +gem install creek +``` + +To use it in Rails, add this line to your Gemfile: + +```ruby +gem 'creek' +``` + +## Basic Usage +Creek can simply parse an Excel file by looping through the rows enumerator: + +```ruby +require 'creek' +creek = Creek::Book.new 'specs/fixtures/sample.xlsx' +sheet= creek.sheets[0] + +sheet.rows.each do |row| + puts row # => {"A1"=>"Content 1", "B1"=>nil, C1"=>nil, "D1"=>"Content 3"} +end + +sheet.rows_with_meta_data.each do |row| + puts row # => {"collapsed"=>"false", "customFormat"=>"false", "customHeight"=>"true", "hidden"=>"false", "ht"=>"12.1", "outlineLevel"=>"0", "r"=>"1", "cells"=>{"A1"=>"Content 1", "B1"=>nil, C1"=>nil, "D1"=>"Content 3"}} +end + +sheet.state # => 'visible' +sheet.name # => 'Sheet1' +sheet.rid # => 'rId2' +``` + +## Filename considerations +By default, Creek will ensure that the file extension is either *.xlsx or *.xlsm, but this check can be circumvented as needed: + +```ruby +path = 'sample-as-zip.zip' +Creek::Book.new path, :check_file_extension => false +``` + +By default, the Rails [file_field_tag](http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-file_field_tag) uploads to a temporary location and stores the original filename with the StringIO object. (See [this section](http://guides.rubyonrails.org/form_helpers.html#uploading-files) of the Rails Guides for more information.) + +Creek can parse this directly without the need for file upload gems such as Carrierwave or Paperclip by passing the original filename as an option: + +```ruby +# Import endpoint in Rails controller +def import + file = params[:file] + Creek::Book.new file.path, check_file_extension: false +end +``` + +## Parsing images +Creek does not parse images by default. If you want to parse the images, +use `with_images` method before iterating over rows to preload images information. If you don't call this method, Creek will not return images anywhere. + +Cells with images will be an array of Pathname objects. +If an image is spread across multiple cells, same Pathname object will be returned for each cell. + +```ruby +sheet.with_images.rows.each do |row| + puts row # => {"A1"=>[#], "B2"=>"Fluffy"} +end +``` + +Images for a specific cell can be obtained with images_at method: + +```ruby +puts sheet.images_at('A1') # => [#] + +# no images in a cell +puts sheet.images_at('C1') # => nil +``` + +Creek will most likely return nil for a cell with images if there is no other text cell in that row - you can use *images_at* method for retrieving images in that cell. + +## Remote files + +```ruby +remote_url = 'http://dev-builds.libreoffice.org/tmp/test.xlsx' +Creek::Book.new remote_url, remote: true +``` + +## Contributing + +Contributions are welcomed. You can fork a repository, add your code changes to the forked branch, ensure all existing unit tests pass, create new unit tests which cover your new changes and finally create a pull request. + +After forking and then cloning the repository locally, install the Bundler and then use it +to install the development gem dependencies: + +``` +gem install bundler +bundle install +``` + +Once this is complete, you should be able to run the test suite: + +``` +rake +``` + +## Bug Reporting + +Please use the [Issues](https://github.com/pythonicrubyist/creek/issues) page to report bugs or suggest new enhancements. + + +## License + +Creek has been published under [MIT License](https://github.com/pythonicrubyist/creek/blob/master/LICENSE.txt) diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 76cd364..0000000 --- a/README.rdoc +++ /dev/null @@ -1,102 +0,0 @@ -= Creek -- Stream parser for large Excel(xlsx and xlsm) files. - -Creek is a Ruby gem that provide a fast, simple and efficient method of parsing large Excel(xlsx and xlsm) files. - - -== Installation - -Creek can be used from the command line or as part of a Ruby web framework. To install the gem using terminal, run the following command: - - gem install creek - -To use it in Rails, add this line to your Gemfile: - - gem "creek" - - -== Basic Usage -Creek can simply parse an Excel file by looping through the rows enumerator: - - require 'creek' - creek = Creek::Book.new "specs/fixtures/sample.xlsx" - sheet= creek.sheets[0] - - sheet.rows.each do |row| - puts row # => {"A1"=>"Content 1", "B1"=>nil, C1"=>nil, "D1"=>"Content 3"} - end - - - sheet.rows_with_meta_data.each do |row| - puts row # => {"collapsed"=>"false", "customFormat"=>"false", "customHeight"=>"true", "hidden"=>"false", "ht"=>"12.1", "outlineLevel"=>"0", "r"=>"1", "cells"=>{"A1"=>"Content 1", "B1"=>nil, C1"=>nil, "D1"=>"Content 3"}} - end - - - sheet.state # => 'visible' - sheet.name # => 'Sheet1' - sheet.rid # => 'rId2' - -== Filename considerations -By default, Creek will ensure that the file extension is either *.xlsx or *.xlsm, but this check can be circumvented as needed: - - path = 'sample-as-zip.zip' - Creek::Book.new path, :check_file_extension => false - -By default, the Rails {file_field_tag}[http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-file_field_tag] uploads to a temporary location and stores the original filename with the StringIO object. (See {this section}[http://guides.rubyonrails.org/form_helpers.html#uploading-files] of the Rails Guides for more information.) - -Creek can parse this directly without the need for file upload gems such as Carrierwave or Paperclip by passing the original filename as an option: - - # Import endpoint in Rails controller - def import - file = params[:file] - Creek::Book.new file.path, check_file_extension: false - end - -== Parsing images -Creek does not parse images by default. If you want to parse the images, -use *with_images* method before iterating over rows to preload images information. If you don't call this method, Creek will not return images anywhere. - - -Cells with images will be an array of Pathname objects. -If an image is spread across multiple cells, same Pathname object will be returned for each cell. - - sheet.with_images.rows.each do |row| - puts row # => {"A1"=>[#], "B2"=>"Fluffy"} - end - - -Images for a specific cell can be obtained with images_at method: - puts sheet.images_at('A1') # => [#] - - # no images in a cell - puts sheet.images_at('C1') # => nil - -Creek will most likely return nil for a cell with images if there is no other text cell in that row - you can use *images_at* method for retrieving images in that cell. - -== Remote files - - remote_url = 'http://dev-builds.libreoffice.org/tmp/test.xlsx' - Creek::Book.new remote_url, remote: true - -== Contributing - -Contributions are welcomed. You can fork a repository, add your code changes to the forked branch, ensure all existing unit tests pass, create new unit tests cover your new changes and finally create a pull request. - -After forking and then cloning the repository locally, install Bundler and then use it -to install the development gem dependecies: - - gem install bundler - bundle install - -Once this is complete, you should be able to run the test suite: - - rake - - -== Bug Reporting - -Please use the {Issues}[https://github.com/pythonicrubyist/creek/issues] page to report bugs or suggest new enhancements. - - -== License - -Creek has been published under {MIT License}[https://github.com/pythonicrubyist/creek/blob/master/LICENSE.txt] From 04a8df26e0dc957264ce6b36a1da0597b201d394 Mon Sep 17 00:00:00 2001 From: Simon Grondin Date: Tue, 13 Jun 2017 18:40:19 -0500 Subject: [PATCH 13/61] All the fixes from the Creek forks out there, and more (#47) * Update nokogiri version Needed to upgrade to Ruby 2.4 * Updated rspec version so tests can pass. * Change to use rspec 3 syntax * Convert specs to RSpec 3.6.0 syntax with Transpec This conversion is done by Transpec 3.3.0 with the following command: transpec * 25 conversions from: obj.should to: expect(obj).to * 22 conversions from: == expected to: eq(expected) * 1 conversion from: obj.should_not to: expect(obj).not_to For more details: https://github.com/yujinakayama/transpec#supported-conversions * Added logic to contend with the default namespace having a prefix Added logic to contend with the default namespace having a prefix. Only addressed on the 'book' * Handle sheets files already containing xl/ prefix Sheets generated by certain libraries already prefix xl/ * Bump httparty to 0.15 and Ruby to >= 2.0.0 Plenty of bugfixes were committed to httparty since 0.14 Some of those fixes address very serious bugs and memory leaks Httparty 0.15 requires Ruby >= 2.0.0, and now so does this gem * support for inlineStr cell type --- creek.gemspec | 8 ++--- lib/creek/book.rb | 11 ++++++- lib/creek/sheet.rb | 6 +++- spec/shared_string_spec.rb | 12 +++---- spec/styles/converter_spec.rb | 2 +- spec/styles/style_types_spec.rb | 6 ++-- spec/test_spec.rb | 55 ++++++++++++++++++--------------- 7 files changed, 59 insertions(+), 41 deletions(-) diff --git a/creek.gemspec b/creek.gemspec index 8532792..ab5f69d 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -18,14 +18,14 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.required_ruby_version = '>= 1.9.2' + spec.required_ruby_version = '>= 2.0.0' spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency "rake" - spec.add_development_dependency 'rspec', '~> 2.13.0' + spec.add_development_dependency 'rspec', '~> 3.6.0' spec.add_development_dependency 'pry' - spec.add_dependency 'nokogiri', '~> 1.6.0' + spec.add_dependency 'nokogiri', '~> 1.7.0' spec.add_dependency 'rubyzip', '>= 1.0.0' - spec.add_dependency 'httparty', '~> 0.14.0' + spec.add_dependency 'httparty', '~> 0.15.5' end diff --git a/lib/creek/book.rb b/lib/creek/book.rb index 558ed81..8c51151 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -34,9 +34,18 @@ def initialize path, options = {} def sheets doc = @files.file.open "xl/workbook.xml" xml = Nokogiri::XML::Document.parse doc + namespaces = xml.namespaces + + cssPrefix = '' + namespaces.each do |namespace| + if namespace[1] == 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' && namespace[0] != 'xmlns' then + cssPrefix = namespace[0].split(':')[1]+'|' + end + end + rels_doc = @files.file.open "xl/_rels/workbook.xml.rels" rels = Nokogiri::XML::Document.parse(rels_doc).css("Relationship") - @sheets = xml.css('sheet').map do |sheet| + @sheets = xml.css(cssPrefix+'sheet').map do |sheet| sheetfile = rels.find { |el| sheet.attr("r:id") == el.attr("Id") }.attr("Target") Sheet.new(self, sheet.attr("name"), sheet.attr("sheetid"), sheet.attr("state"), sheet.attr("visible"), sheet.attr("r:id"), sheetfile) end diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index b83a14d..530e186 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -66,7 +66,7 @@ def rows_with_meta_data # Returns a hash per row that includes the cell ids and values. # Empty cells will be also included in the hash with a nil value. def rows_generator include_meta_data=false - path = "xl/#{@sheetfile}" + path = if @sheetfile.start_with? "/xl/" or @sheetfile.start_with? "xl/" then @sheetfile else "xl/#{@sheetfile}" end if @book.files.file.exist?(path) # SAX parsing, Each element in the stream comes through as two events: # one to open the element and one to close it. @@ -103,6 +103,10 @@ def rows_generator include_meta_data=false unless cell.nil? cells[cell] = convert(node.inner_xml, cell_type, cell_style_idx) end + elsif (node.name.eql? 't') and (node.node_type.eql? opener) + unless cell.nil? + cells[cell] = convert(node.inner_xml, cell_type, cell_style_idx) + end end end end diff --git a/spec/shared_string_spec.rb b/spec/shared_string_spec.rb index b8f5edc..3eeb0f4 100644 --- a/spec/shared_string_spec.rb +++ b/spec/shared_string_spec.rb @@ -7,12 +7,12 @@ doc = Nokogiri::XML(shared_strings_xml_file) dictionary = Creek::SharedStrings.parse_shared_string_from_document(doc) - dictionary.keys.size.should == 5 - dictionary[0].should == 'Cell A1' - dictionary[1].should == 'Cell B1' - dictionary[2].should == 'My Cell' - dictionary[3].should == 'Cell A2' - dictionary[4].should == 'Cell B2' + expect(dictionary.keys.size).to eq(5) + expect(dictionary[0]).to eq('Cell A1') + expect(dictionary[1]).to eq('Cell B1') + expect(dictionary[2]).to eq('My Cell') + expect(dictionary[3]).to eq('Cell A2') + expect(dictionary[4]).to eq('Cell B2') end end \ No newline at end of file diff --git a/spec/styles/converter_spec.rb b/spec/styles/converter_spec.rb index f5362ba..270c9ad 100644 --- a/spec/styles/converter_spec.rb +++ b/spec/styles/converter_spec.rb @@ -9,7 +9,7 @@ def convert(value, type, style) describe :date_time do it "works" do - convert('41275', 'n', :date_time).should == Date.new(2013,01,01) + expect(convert('41275', 'n', :date_time)).to eq(Date.new(2013,01,01)) end end end diff --git a/spec/styles/style_types_spec.rb b/spec/styles/style_types_spec.rb index 133c42d..b5c72bc 100644 --- a/spec/styles/style_types_spec.rb +++ b/spec/styles/style_types_spec.rb @@ -7,9 +7,9 @@ xml_file = File.open('spec/fixtures/styles/first.xml') doc = Nokogiri::XML(xml_file) res = Creek::Styles::StyleTypes.new(doc).call - res.size.should == 8 - res[3].should == :date_time - res.should == [:unsupported, :unsupported, :unsupported, :date_time, :unsupported, :unsupported, :unsupported, :unsupported] + expect(res.size).to eq(8) + expect(res[3]).to eq(:date_time) + expect(res).to eq([:unsupported, :unsupported, :unsupported, :date_time, :unsupported, :unsupported, :unsupported, :unsupported]) end end end diff --git a/spec/test_spec.rb b/spec/test_spec.rb index eec100d..491ff53 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -2,33 +2,38 @@ describe 'Creek trying to parsing an invalid file.' do it 'Fail to open a legacy xls file.' do - lambda { Creek::Book.new 'spec/fixtures/invalid.xls' }.should raise_error 'Not a valid file format.' + expect { Creek::Book.new 'spec/fixtures/invalid.xls' } + .to raise_error 'Not a valid file format.' end it 'Ignore file extensions on request.' do path = 'spec/fixtures/sample-as-zip.zip' - lambda { Creek::Book.new path, :check_file_extension => false }.should_not raise_error + expect { Creek::Book.new path, check_file_extension: false } + .not_to raise_error end it 'Check file extension when requested.' do - open_book = lambda { Creek::Book.new 'spec/fixtures/invalid.xls', :check_file_extension => true } - open_book.should raise_error 'Not a valid file format.' + expect { Creek::Book.new 'spec/fixtures/invalid.xls', check_file_extension: true } + .to raise_error 'Not a valid file format.' end it 'Fail to open remote file' do - lambda { Creek::Book.new 'http://dev-builds.libreoffice.org/tmp/test.xlsx' }.should raise_error + expect { Creek::Book.new 'http://dev-builds.libreoffice.org/tmp/test.xlsx' } + .to raise_error(Zip::Error, /not found/) end it 'Opens remote file with remote flag' do - lambda { Creek::Book.new 'http://dev-builds.libreoffice.org/tmp/test.xlsx', remote: true }.should_not raise_error + expect { Creek::Book.new 'http://dev-builds.libreoffice.org/tmp/test.xlsx', remote: true } + .not_to raise_error end it 'Check file extension of original_filename if passed.' do path = 'spec/fixtures/temp_string_io_file_path_with_no_extension' - lambda { Creek::Book.new path, :original_filename => 'invalid.xls' }.should raise_error 'Not a valid file format.' - lambda { Creek::Book.new path, :original_filename => 'valid.xlsx' }.should_not raise_error + expect { Creek::Book.new path, :original_filename => 'invalid.xls' } + .to raise_error 'Not a valid file format.' + expect { Creek::Book.new path, :original_filename => 'valid.xlsx' } + .not_to raise_error end - end describe 'Creek parsing a sample XLSX file' do @@ -49,15 +54,15 @@ end it 'open an XLSX file successfully.' do - @creek.should_not be_nil + expect(@creek).not_to be_nil end it 'find sheets successfully.' do - @creek.sheets.count.should == 1 + expect(@creek.sheets.count).to eq(1) sheet = @creek.sheets.first - sheet.state.should eql nil - sheet.name.should eql 'Sheet1' - sheet.rid.should eql 'rId1' + expect(sheet.state).to eql nil + expect(sheet.name).to eql 'Sheet1' + expect(sheet.rid).to eql 'rId1' end it 'Parse rows with empty cells successfully.' do @@ -68,16 +73,16 @@ row_count += 1 end - rows[0].should == @expected_rows[0] - rows[1].should == @expected_rows[1] - rows[2].should == @expected_rows[2] - rows[3].should == @expected_rows[3] - rows[4].should == @expected_rows[4] - rows[5].should == @expected_rows[5] - rows[6].should == @expected_rows[6] - rows[7].should == @expected_rows[7] - rows[8].should == @expected_rows[8] - row_count.should == 9 + expect(rows[0]).to eq(@expected_rows[0]) + expect(rows[1]).to eq(@expected_rows[1]) + expect(rows[2]).to eq(@expected_rows[2]) + expect(rows[3]).to eq(@expected_rows[3]) + expect(rows[4]).to eq(@expected_rows[4]) + expect(rows[5]).to eq(@expected_rows[5]) + expect(rows[6]).to eq(@expected_rows[6]) + expect(rows[7]).to eq(@expected_rows[7]) + expect(rows[8]).to eq(@expected_rows[8]) + expect(row_count).to eq(9) end it 'Parse rows with empty cells and meta data successfully.' do @@ -87,6 +92,6 @@ rows << row row_count += 1 end - rows.map{|r| r['cells']}.should == @expected_rows + expect(rows.map{|r| r['cells']}).to eq(@expected_rows) end end From dcbb78fd79632de58059ba452d6d7444647c8815 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Tue, 13 Jun 2017 20:15:33 -0400 Subject: [PATCH 14/61] issue #42 fixed. --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index 23d448f..2e951ec 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2013 TODO: Write your name +Copyright (c) 2017 Ramtin Vaziri MIT License From 5b0033af876276990e4a6d853a8309dfe0f11b16 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Tue, 13 Jun 2017 20:19:50 -0400 Subject: [PATCH 15/61] Gem version bumped to 2.0. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 0065afd..e91fe4f 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "1.1.2" + VERSION = "2.0" end From 260c196428267bbbda59908bbfe1697c9a1ed482 Mon Sep 17 00:00:00 2001 From: kruzewski Date: Mon, 28 Aug 2017 09:49:07 -0400 Subject: [PATCH 16/61] Master (#48) * Added logic to contend with the default namespace having a prefix Added logic to contend with the default namespace having a prefix. Only addressed on the 'book' * cleaned up an irrelevant puts statment From cab8978d886f4526bec4330399480dd355109af0 Mon Sep 17 00:00:00 2001 From: "Harry V. Kiselev" Date: Mon, 28 Aug 2017 16:51:27 +0300 Subject: [PATCH 17/61] Update README.md: additional space symbol. (#50) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a06b396..5ea4d09 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Creek can simply parse an Excel file by looping through the rows enumerator: ```ruby require 'creek' creek = Creek::Book.new 'specs/fixtures/sample.xlsx' -sheet= creek.sheets[0] +sheet = creek.sheets[0] sheet.rows.each do |row| puts row # => {"A1"=>"Content 1", "B1"=>nil, C1"=>nil, "D1"=>"Content 3"} From b83a6bf4eb74b760fdf371a2e6438c1dabad68e5 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Mon, 28 Aug 2017 11:01:11 -0400 Subject: [PATCH 18/61] Remove specs with dependency on external files. --- spec/test_spec.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 491ff53..062e6b3 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -17,16 +17,6 @@ .to raise_error 'Not a valid file format.' end - it 'Fail to open remote file' do - expect { Creek::Book.new 'http://dev-builds.libreoffice.org/tmp/test.xlsx' } - .to raise_error(Zip::Error, /not found/) - end - - it 'Opens remote file with remote flag' do - expect { Creek::Book.new 'http://dev-builds.libreoffice.org/tmp/test.xlsx', remote: true } - .not_to raise_error - end - it 'Check file extension of original_filename if passed.' do path = 'spec/fixtures/temp_string_io_file_path_with_no_extension' expect { Creek::Book.new path, :original_filename => 'invalid.xls' } From f4613bb3ed403ffe962c51de8b6f05e56dcfe6d3 Mon Sep 17 00:00:00 2001 From: Aralox Date: Mon, 27 Nov 2017 08:23:46 +1100 Subject: [PATCH 19/61] =?UTF-8?q?Updated=20gemspec=20to=20use=20latest=20v?= =?UTF-8?q?ersion=20of=20nokogiri=20(1.8.0)=20which=20works=E2=80=A6=20(#5?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated gemspec to use latest version of nokogiri (1.8.0) which works with Ruby 2.4. Modified example code slightly in README to match project folder name ('spec' instead of 'specs'). * Updated rubyzip version. --- README.md | 2 +- creek.gemspec | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5ea4d09..c17f155 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Creek can simply parse an Excel file by looping through the rows enumerator: ```ruby require 'creek' -creek = Creek::Book.new 'specs/fixtures/sample.xlsx' +creek = Creek::Book.new 'spec/fixtures/sample.xlsx' sheet = creek.sheets[0] sheet.rows.each do |row| diff --git a/creek.gemspec b/creek.gemspec index ab5f69d..1887770 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'rspec', '~> 3.6.0' spec.add_development_dependency 'pry' - spec.add_dependency 'nokogiri', '~> 1.7.0' - spec.add_dependency 'rubyzip', '>= 1.0.0' + spec.add_dependency 'nokogiri', '~> 1.8.0' + spec.add_dependency 'rubyzip', '>= 1.2.1' spec.add_dependency 'httparty', '~> 0.15.5' end From 9be2b1eaa20bae53ba20d113a6ba50224ad2b1eb Mon Sep 17 00:00:00 2001 From: Weston Ganger Date: Sun, 26 Nov 2017 13:38:46 -0800 Subject: [PATCH 20/61] relax nokogiri version restrictions (#58) --- creek.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/creek.gemspec b/creek.gemspec index 1887770..b82acde 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'rspec', '~> 3.6.0' spec.add_development_dependency 'pry' - spec.add_dependency 'nokogiri', '~> 1.8.0' - spec.add_dependency 'rubyzip', '>= 1.2.1' + spec.add_dependency 'nokogiri', '>= 1.7.0' + spec.add_dependency 'rubyzip', '>= 1.0.0' spec.add_dependency 'httparty', '~> 0.15.5' end From 4a6e491838cae295b9801e4bac596986bce1baaa Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Sun, 26 Nov 2017 17:04:56 -0500 Subject: [PATCH 21/61] Gem version bumped to 2.1. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index e91fe4f..39ba36a 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.0" + VERSION = "2.1" end From 8f715d8971b9a7848ec093d95e3ca001c9a4bb08 Mon Sep 17 00:00:00 2001 From: Blake Gentry Date: Mon, 23 Apr 2018 12:42:48 -0700 Subject: [PATCH 22/61] Replace HTTParty with http.rb (#64) * replace HTTParty with http.rb in gemspec * replace httparty use with http.rb --- creek.gemspec | 2 +- lib/creek/book.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/creek.gemspec b/creek.gemspec index b82acde..377aa36 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -27,5 +27,5 @@ Gem::Specification.new do |spec| spec.add_dependency 'nokogiri', '>= 1.7.0' spec.add_dependency 'rubyzip', '>= 1.0.0' - spec.add_dependency 'httparty', '~> 0.15.5' + spec.add_dependency 'http', '~> 3.0.0' end diff --git a/lib/creek/book.rb b/lib/creek/book.rb index 8c51151..613cac6 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -1,7 +1,7 @@ require 'zip/filesystem' require 'nokogiri' require 'date' -require 'httparty' +require 'http' module Creek @@ -23,7 +23,7 @@ def initialize path, options = {} if options[:remote] zipfile = Tempfile.new("file") zipfile.binmode - zipfile.write(HTTParty.get(path).body) + zipfile.write(HTTP.get(path).to_s) zipfile.close path = zipfile.path end From ce7e21af8d454a430c8d3c137feae5c742003ecc Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Mon, 23 Apr 2018 16:00:21 -0400 Subject: [PATCH 23/61] Gem version bumped to 2.2. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 39ba36a..dfd7e30 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.1" + VERSION = "2.2" end From 801a8fa9c2d8e48c5f54f79183eca4eda9e36f67 Mon Sep 17 00:00:00 2001 From: David Arango Date: Wed, 16 May 2018 17:22:35 +0200 Subject: [PATCH 24/61] Fix inconsistent DateTime conversion (#65) * Always return a Time object for DateTime cells * Fix specs for DateTime parsing --- lib/creek/styles/converter.rb | 37 ++++++++++------ spec/fixtures/sample_dates.xlsx | Bin 0 -> 9358 bytes spec/fixtures/sheets/sample_dates.xlsx | Bin 0 -> 6495 bytes .../sheets/single_data_programme.xlsx | Bin 0 -> 6948 bytes spec/styles/converter_spec.rb | 9 +++- spec/test_spec.rb | 40 ++++++++++++++---- 6 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 spec/fixtures/sample_dates.xlsx create mode 100644 spec/fixtures/sheets/sample_dates.xlsx create mode 100644 spec/fixtures/sheets/single_data_programme.xlsx diff --git a/lib/creek/styles/converter.rb b/lib/creek/styles/converter.rb index 01a54e6..459d3fc 100644 --- a/lib/creek/styles/converter.rb +++ b/lib/creek/styles/converter.rb @@ -60,8 +60,10 @@ def self.call(value, type, style, options = {}) value.to_i when :float, :percentage value.to_f - when :date, :time, :date_time + when :date convert_date(value, options) + when :time, :date_time + convert_datetime(value, options) when :bignum convert_bignum(value) @@ -71,22 +73,17 @@ def self.call(value, type, style, options = {}) end end - # the trickiest. note that all these formats can vary on - # whether they actually contain a date, time, or datetime. def self.convert_date(value, options) - value = value.to_f - days_since_date_system_start = value.to_i - fraction_of_24 = value - days_since_date_system_start + date = base_date(options) + value.to_i + yyyy, mm, dd = date.strftime('%Y-%m-%d').split('-') - # http://stackoverflow.com/questions/10559767/how-to-convert-ms-excel-date-from-float-to-date-format-in-ruby - date = options.fetch(:base_date, Date.new(1899, 12, 30)) + days_since_date_system_start + ::Date.new(yyyy.to_i, mm.to_i, dd.to_i) + end - if fraction_of_24 > 0 # there is a time associated - seconds = (fraction_of_24 * 86400).round - return Time.utc(date.year, date.month, date.day) + seconds - else - return date - end + def self.convert_datetime(value, options) + date = base_date(options) + value.to_f.round(6) + + round_datetime(date.strftime('%Y-%m-%d %H:%M:%S.%N')) end def self.convert_bignum(value) @@ -96,6 +93,18 @@ def self.convert_bignum(value) value.to_f end end + + private + + def self.base_date(options) + options.fetch(:base_date, Date.new(1899, 12, 30)) + end + + def self.round_datetime(datetime_string) + /(?\d+)-(?\d+)-(?
\d+) (?\d+):(?\d+):(?\d+.\d+)/ =~ datetime_string + + ::Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0) + end end end end diff --git a/spec/fixtures/sample_dates.xlsx b/spec/fixtures/sample_dates.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..e4edea546d1589ab1dafb3713dbb09831bfe4f9c GIT binary patch literal 9358 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 JYuexb{XdS=N^$@I literal 0 HcmV?d00001 diff --git a/spec/fixtures/sheets/sample_dates.xlsx b/spec/fixtures/sheets/sample_dates.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0a5d92fb3b9cc6ff513f9189804cfba274ae3680 GIT binary patch literal 6495 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 literal 0 HcmV?d00001 diff --git a/spec/fixtures/sheets/single_data_programme.xlsx b/spec/fixtures/sheets/single_data_programme.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..468537f9fcaa2dfb4311a3177ddafe9b871c4465 GIT binary patch literal 6948 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 b1O8i{&{RP~BoP1r8}SK6#0o 'Date', 'B3' => Date.parse('2018-01-01')}, + {'A4' => 'Datetime 00:00:00', 'B4' => Time.parse('2018-01-01 00:00:00')}, + {'A5' => 'Datetime', 'B5' => Time.parse('2018-01-01 23:59:59')}] + end + + after(:all) do + @creek.close + end + + it 'parses dates successfully' do + rows = Array.new + row_count = 0 + @creek.sheets[0].rows.each do |row| + rows << row + row_count += 1 + end + + (2..5).each do |number| + expect(rows[number]).to eq(@expected_datetime_rows[number-2]) + end + end +end + describe 'Creek parsing a sample XLSX file' do before(:all) do @creek = Creek::Book.new 'spec/fixtures/sample.xlsx' @@ -63,15 +91,9 @@ row_count += 1 end - expect(rows[0]).to eq(@expected_rows[0]) - expect(rows[1]).to eq(@expected_rows[1]) - expect(rows[2]).to eq(@expected_rows[2]) - expect(rows[3]).to eq(@expected_rows[3]) - expect(rows[4]).to eq(@expected_rows[4]) - expect(rows[5]).to eq(@expected_rows[5]) - expect(rows[6]).to eq(@expected_rows[6]) - expect(rows[7]).to eq(@expected_rows[7]) - expect(rows[8]).to eq(@expected_rows[8]) + (0..8).each do |number| + expect(rows[number]).to eq(@expected_rows[number]) + end expect(row_count).to eq(9) end From 1ae72a1ec5d9f52bfd4df175a98f629270819932 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Fri, 13 Jul 2018 13:12:05 -0400 Subject: [PATCH 25/61] Bump up gem version. --- creek.gemspec | 2 +- lib/creek/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/creek.gemspec b/creek.gemspec index 377aa36..07d44f9 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -27,5 +27,5 @@ Gem::Specification.new do |spec| spec.add_dependency 'nokogiri', '>= 1.7.0' spec.add_dependency 'rubyzip', '>= 1.0.0' - spec.add_dependency 'http', '~> 3.0.0' + spec.add_dependency 'http', '~> 3.0' end diff --git a/lib/creek/version.rb b/lib/creek/version.rb index dfd7e30..c458776 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.2" + VERSION = "2.3" end From dcae13e4b88664a9f7742d3760ac09d5413da5ac Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Fri, 13 Jul 2018 14:40:16 -0400 Subject: [PATCH 26/61] [ creek #61 ] implemented. --- README.md | 12 ++++++++++-- lib/creek/sheet.rb | 35 +++++++++++++++++++++++------------ lib/creek/version.rb | 2 +- spec/test_spec.rb | 24 ++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index c17f155..0dc9706 100644 --- a/README.md +++ b/README.md @@ -26,11 +26,19 @@ creek = Creek::Book.new 'spec/fixtures/sample.xlsx' sheet = creek.sheets[0] sheet.rows.each do |row| - puts row # => {"A1"=>"Content 1", "B1"=>nil, C1"=>nil, "D1"=>"Content 3"} + puts row # => {"A1"=>"Content 1", "B1"=>nil, "C1"=>nil, "D1"=>"Content 3"} +end + +sheet.simple_rows.each do |row| + puts row # => {"A"=>"Content 1", "B"=>nil, "C"=>nil, "D"=>"Content 3"} end sheet.rows_with_meta_data.each do |row| - puts row # => {"collapsed"=>"false", "customFormat"=>"false", "customHeight"=>"true", "hidden"=>"false", "ht"=>"12.1", "outlineLevel"=>"0", "r"=>"1", "cells"=>{"A1"=>"Content 1", "B1"=>nil, C1"=>nil, "D1"=>"Content 3"}} + puts row # => {"collapsed"=>"false", "customFormat"=>"false", "customHeight"=>"true", "hidden"=>"false", "ht"=>"12.1", "outlineLevel"=>"0", "r"=>"1", "cells"=>{"A1"=>"Content 1", "B1"=>nil, "C1"=>nil, "D1"=>"Content 3"}} +end + +sheet.simple_rows_with_meta_data.each do |row| + puts row # => {"collapsed"=>"false", "customFormat"=>"false", "customHeight"=>"true", "hidden"=>"false", "ht"=>"12.1", "outlineLevel"=>"0", "r"=>"1", "cells"=>{"A"=>"Content 1", "B"=>nil, "C"=>nil, "D"=>"Content 3"}} end sheet.state # => 'visible' diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index 530e186..7601401 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -46,18 +46,33 @@ def images_at(cell) @drawing.images_at(cell) if @images_present end + + ## + # Provides an Enumerator that returns a hash representing each row. + # The key of the hash is the column ID and the value is the value of the cell. + def simple_rows + rows_generator false, true + end + ## # Provides an Enumerator that returns a hash representing each row. # The key of the hash is the Cell id and the value is the value of the cell. def rows - rows_generator + rows_generator false, false end ## # Provides an Enumerator that returns a hash representing each row. # The hash contains meta data of the row and a 'cells' embended hash which contains the cell contents. def rows_with_meta_data - rows_generator true + rows_generator true, false + end + + ## + # Provides an Enumerator that returns a hash representing each row. + # The hash contains meta data of the row and a 'cells' embended hash which contains the cell contents. + def simple_rows_with_meta_data + rows_generator true, true end private @@ -65,7 +80,7 @@ def rows_with_meta_data ## # Returns a hash per row that includes the cell ids and values. # Empty cells will be also included in the hash with a nil value. - def rows_generator include_meta_data=false + def rows_generator include_meta_data=false, use_simple_rows_format=false path = if @sheetfile.start_with? "/xl/" or @sheetfile.start_with? "xl/" then @sheetfile else "xl/#{@sheetfile}" end if @book.files.file.exist?(path) # SAX parsing, Each element in the stream comes through as two events: @@ -84,7 +99,7 @@ def rows_generator include_meta_data=false cells = Hash.new y << (include_meta_data ? row : cells) if node.self_closing? elsif (node.name.eql? 'row') and (node.node_type.eql? closer) - processed_cells = fill_in_empty_cells(cells, row['r'], cell) + processed_cells = fill_in_empty_cells(cells, row['r'], cell, use_simple_rows_format) if @images_present processed_cells.each do |cell_name, cell_value| @@ -99,13 +114,9 @@ def rows_generator include_meta_data=false cell_type = node.attributes['t'] cell_style_idx = node.attributes['s'] cell = node.attributes['r'] - elsif (node.name.eql? 'v') and (node.node_type.eql? opener) - unless cell.nil? - cells[cell] = convert(node.inner_xml, cell_type, cell_style_idx) - end - elsif (node.name.eql? 't') and (node.node_type.eql? opener) + elsif (['v', 't'].include? node.name) and (node.node_type.eql? opener) unless cell.nil? - cells[cell] = convert(node.inner_xml, cell_type, cell_style_idx) + cells[(use_simple_rows_format ? cell.tr("0-9", "") : cell)] = convert(node.inner_xml, cell_type, cell_style_idx) end end end @@ -129,14 +140,14 @@ def converter_options ## # The unzipped XML file does not contain any node for empty cells. # Empty cells are being padded in using this function - def fill_in_empty_cells(cells, row_number, last_col) + def fill_in_empty_cells(cells, row_number, last_col, use_simple_rows_format) new_cells = Hash.new unless cells.empty? last_col = last_col.gsub(row_number, '') ("A"..last_col).to_a.each do |column| - id = "#{column}#{row_number}" + id = use_simple_rows_format ? "#{column}" : "#{column}#{row_number}" new_cells[id] = cells[id] end end diff --git a/lib/creek/version.rb b/lib/creek/version.rb index c458776..a98a214 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.3" + VERSION = "2.4" end diff --git a/spec/test_spec.rb b/spec/test_spec.rb index d1f648a..97392ec 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -65,6 +65,16 @@ {'A6'=>'1', 'B6'=>'2', 'C6'=>'3'}, {'A7'=>'Content 15', 'B7'=>'Content 16', 'C7'=>'Content 18', 'D7'=>'Content 19'}, {'A8'=>nil, 'B8'=>'Content 20', 'C8'=>nil, 'D8'=>nil, 'E8'=>nil, 'F8'=>'Content 21'}, {'A10' => 0.15, 'B10' => 0.15}] + + @expected_simple_rows = [{"A"=>"Content 1", "B"=>nil, "C"=>"Content 2", "D"=>nil, "E"=>"Content 3"}, + {"A"=>nil, "B"=>"Content 4", "C"=>nil, "D"=>"Content 5", "E"=>nil, "F"=>"Content 6"}, + {}, + {"A"=>"Content 7", "B"=>"Content 8", "C"=>"Content 9", "D"=>"Content 10", "E"=>"Content 11", "F"=>"Content 12"}, + {"A"=>nil, "B"=>nil, "C"=>nil, "D"=>nil, "E"=>nil, "F"=>nil, "G"=>nil, "H"=>nil, "I"=>nil, "J"=>nil, "K"=>nil, "L"=>nil, "M"=>nil, "N"=>nil, "O"=>nil, "P"=>nil, "Q"=>nil, "R"=>nil, "S"=>nil, "T"=>nil, "U"=>nil, "V"=>nil, "W"=>nil, "X"=>nil, "Y"=>nil, "Z"=>"Z Content", "AA"=>nil, "AB"=>nil, "AC"=>nil, "AD"=>nil, "AE"=>nil, "AF"=>nil, "AG"=>nil, "AH"=>nil, "AI"=>nil, "AJ"=>nil, "AK"=>nil, "AL"=>nil, "AM"=>nil, "AN"=>nil, "AO"=>nil, "AP"=>nil, "AQ"=>nil, "AR"=>nil, "AS"=>nil, "AT"=>nil, "AU"=>nil, "AV"=>nil, "AW"=>nil, "AX"=>nil, "AY"=>nil, "AZ"=>"Content 13"}, + {"A"=>"1", "B"=>"2", "C"=>"3"}, + {"A"=>"Content 15", "B"=>"Content 16", "C"=>"Content 18", "D"=>"Content 19"}, + {"A"=>nil, "B"=>"Content 20", "C"=>nil, "D"=>nil, "E"=>nil, "F"=>"Content 21"}, + {"A"=>0.15, "B"=>0.15}] end after(:all) do @@ -83,6 +93,20 @@ expect(sheet.rid).to eql 'rId1' end + it 'Parse simple rows successfully.' do + rows = Array.new + row_count = 0 + @creek.sheets[0].simple_rows.each do |row| + rows << row + row_count += 1 + end + (0..8).each do |number| + expect(rows[number]).to eq(@expected_simple_rows[number]) + end + expect(row_count).to eq(9) + end + + it 'Parse rows with empty cells successfully.' do rows = Array.new row_count = 0 From 67f0f22233d8324843bb6afcba40ee3fc64f6351 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Fri, 13 Jul 2018 16:05:57 -0400 Subject: [PATCH 27/61] [ creek #70 ] fixed. --- .DS_Store | Bin 0 -> 6148 bytes lib/creek/styles/converter.rb | 22 ++++++++++++++++++++-- lib/creek/version.rb | 2 +- spec/.DS_Store | Bin 0 -> 6148 bytes spec/fixtures/large_numbers.xlsx | Bin 0 -> 8720 bytes spec/test_spec.rb | 21 +++++++++++++++++++++ 6 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 .DS_Store create mode 100644 spec/.DS_Store create mode 100644 spec/fixtures/large_numbers.xlsx diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 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 literal 0 HcmV?d00001 diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 97392ec..2ba1ebb 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -54,6 +54,27 @@ end end +describe 'Creek parsing a file with large numbrts.' do + before(:all) do + @creek = Creek::Book.new 'spec/fixtures/large_numbers.xlsx' + @expected_simple_rows = [{"A"=>"7.83294732E8", "B"=>"783294732", "C"=>783294732.0}] + end + + after(:all) do + @creek.close + end + + it 'Parse simple rows successfully.' do + rows = Array.new + row_count = 0 + @creek.sheets[0].simple_rows.each do |row| + rows << row + row_count += 1 + end + expect(rows[0]).to eq(@expected_simple_rows[0]) + end +end + describe 'Creek parsing a sample XLSX file' do before(:all) do @creek = Creek::Book.new 'spec/fixtures/sample.xlsx' From 1174baa5990515fd955f65ae16ec27f61f52ef5a Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Fri, 13 Jul 2018 16:42:47 -0400 Subject: [PATCH 28/61] [ creek ] add badges to README. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0dc9706..916abfb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ + +[![version](https://badge.fury.io/rb/creek.svg)](https://badge.fury.io/rb/creek) +[![rubygems.org](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads)](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads) + # Creek - Stream parser for large Excel (xlsx and xlsm) files. Creek is a Ruby gem that provides a fast, simple and efficient method of parsing large Excel (xlsx and xlsm) files. From c1025412533b8cf9f1735d28317f18c14a87b1be Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Fri, 13 Jul 2018 16:43:59 -0400 Subject: [PATCH 29/61] [ creek ] add badges to README. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 916abfb..395861d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - [![version](https://badge.fury.io/rb/creek.svg)](https://badge.fury.io/rb/creek) [![rubygems.org](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads)](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads) From 3f1f724d1d907f2704fce36aac281fde2297c761 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Fri, 13 Jul 2018 16:45:02 -0400 Subject: [PATCH 30/61] [ creek ] add badges to README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 395861d..ea287db 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![version](https://badge.fury.io/rb/creek.svg)](https://badge.fury.io/rb/creek) -[![rubygems.org](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads)](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads) +[![downloads](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads)](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads) # Creek - Stream parser for large Excel (xlsx and xlsm) files. From caf40b6c73ca800e66ce4cf11d8f9298fbf3a57b Mon Sep 17 00:00:00 2001 From: Jarred Holman Date: Tue, 9 Oct 2018 01:42:22 +1030 Subject: [PATCH 31/61] Unescape the special hex code escape sequences (#73) Unescape the special hex code escape sequences (#73) --- lib/creek/shared_strings.rb | 4 ++-- lib/creek/styles/converter.rb | 14 ++++++++++++-- spec/fixtures/sst.xml | 3 +++ spec/shared_string_spec.rb | 3 ++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/creek/shared_strings.rb b/lib/creek/shared_strings.rb index e8baf95..6abd2e3 100644 --- a/lib/creek/shared_strings.rb +++ b/lib/creek/shared_strings.rb @@ -31,9 +31,9 @@ def self.parse_shared_string_from_document(xml) xml.css('si').each_with_index do |si, idx| text_nodes = si.css('t') if text_nodes.count == 1 # plain text node - dictionary[idx] = text_nodes.first.content + dictionary[idx] = Creek::Styles::Converter.unescape_string(text_nodes.first.content) else # rich text nodes with text fragments - dictionary[idx] = text_nodes.map(&:content).join('') + dictionary[idx] = text_nodes.map { |n| Creek::Styles::Converter.unescape_string(n.content) }.join('') end end diff --git a/lib/creek/styles/converter.rb b/lib/creek/styles/converter.rb index 576766a..391861a 100644 --- a/lib/creek/styles/converter.rb +++ b/lib/creek/styles/converter.rb @@ -4,6 +4,10 @@ module Creek class Styles class Converter include Creek::Styles::Constants + + # Excel non-printable character escape sequence + HEX_ESCAPE_REGEXP = /_x[0-9A-Za-z]{4}_/ + ## # The heart of typecasting. The ruby type is determined either explicitly # from the cell xml or implicitly from the cell style, and this @@ -45,9 +49,9 @@ def self.call(value, type, style, options = {}) when 'b' value.to_i == 1 when 'str' - value + unescape_string(value) when 'inlineStr' - value + unescape_string(value) ## # Type can also be determined by a style, @@ -112,6 +116,12 @@ def self.convert_bignum(value) end end + def self.unescape_string(value) + # excel encodes some non-printable characters using a hex code in the format _xHHHH_ + # e.g. Carriage Return (\r) is encoded as _x000D_ + value.gsub(HEX_ESCAPE_REGEXP) { |match| match[2, 4].to_i(16).chr(Encoding::UTF_8) } + end + private def self.base_date(options) diff --git a/spec/fixtures/sst.xml b/spec/fixtures/sst.xml index cd4bd29..112db32 100644 --- a/spec/fixtures/sst.xml +++ b/spec/fixtures/sst.xml @@ -75,4 +75,7 @@ B2 + + Cell with_x000D_escaped_x000D_characters + \ No newline at end of file diff --git a/spec/shared_string_spec.rb b/spec/shared_string_spec.rb index 3eeb0f4..11fbf20 100644 --- a/spec/shared_string_spec.rb +++ b/spec/shared_string_spec.rb @@ -7,12 +7,13 @@ doc = Nokogiri::XML(shared_strings_xml_file) dictionary = Creek::SharedStrings.parse_shared_string_from_document(doc) - expect(dictionary.keys.size).to eq(5) + expect(dictionary.keys.size).to eq(6) expect(dictionary[0]).to eq('Cell A1') expect(dictionary[1]).to eq('Cell B1') expect(dictionary[2]).to eq('My Cell') expect(dictionary[3]).to eq('Cell A2') expect(dictionary[4]).to eq('Cell B2') + expect(dictionary[5]).to eq("Cell with\rescaped\rcharacters") end end \ No newline at end of file From debb898d1764a309b57cee867cddb4cad3d31ef4 Mon Sep 17 00:00:00 2001 From: David Carlin Date: Tue, 12 Mar 2019 20:49:47 +1100 Subject: [PATCH 32/61] Ampersand escaped failing test (#75) * Failing test to show that ampersand gets escaped * Added working example for when xlsx file uses shared strings --- spec/fixtures/escaped.xlsx | Bin 0 -> 3549 bytes spec/fixtures/escaped2.xlsx | Bin 0 -> 8572 bytes spec/sheet_spec.rb | 12 ++++++++++++ 3 files changed, 12 insertions(+) create mode 100644 spec/fixtures/escaped.xlsx create mode 100644 spec/fixtures/escaped2.xlsx diff --git a/spec/fixtures/escaped.xlsx b/spec/fixtures/escaped.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a8ee7724dbdb4eb321de419dca07334fefd8e661 GIT binary patch literal 3549 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 literal 0 HcmV?d00001 diff --git a/spec/sheet_spec.rb b/spec/sheet_spec.rb index 887109e..4d2ef14 100644 --- a/spec/sheet_spec.rb +++ b/spec/sheet_spec.rb @@ -12,6 +12,18 @@ def load_cell(rows, cell_name) cell[cell_name] if cell end + context 'escaped ampersand' do + let(:book_escaped) { Creek::Book.new('spec/fixtures/escaped.xlsx') } + it 'does NOT escape ampersand' do + expect(book_escaped.sheets[0].rows.to_enum.map(&:values)).to eq([["abc", "def"], ["ghi", "j&k"]]) + end + + let(:book_escaped2) { Creek::Book.new('spec/fixtures/escaped2.xlsx') } + it 'does escape ampersand' do + expect(book_escaped2.sheets[0].rows.to_enum.map(&:values)).to eq([["abc", "def"], ["ghi", "j&k"]]) + end + end + describe '#rows' do context 'with excel with images' do context 'with images preloading' do From 74c6c1cfcefadcc48d2348c2c2a669a80fb45f22 Mon Sep 17 00:00:00 2001 From: David Carlin Date: Tue, 12 Mar 2019 20:50:30 +1100 Subject: [PATCH 33/61] Ampersand escaped test fixed (#76) * Failing test to show that ampersand gets escaped * Added working example for when xlsx file uses shared strings * Fix test by getting node value instead of inner_xml --- lib/creek/sheet.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index 7601401..1871206 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -116,7 +116,8 @@ def rows_generator include_meta_data=false, use_simple_rows_format=false cell = node.attributes['r'] elsif (['v', 't'].include? node.name) and (node.node_type.eql? opener) unless cell.nil? - cells[(use_simple_rows_format ? cell.tr("0-9", "") : cell)] = convert(node.inner_xml, cell_type, cell_style_idx) + node.read + cells[(use_simple_rows_format ? cell.tr("0-9", "") : cell)] = convert(node.value, cell_type, cell_style_idx) end end end From 4e1b1b4ab3386619c20b09a453b646a1dc13bb26 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Tue, 12 Mar 2019 06:08:25 -0400 Subject: [PATCH 34/61] bump up gem version. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index c881b11..5f06a37 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.4.1" + VERSION = "2.4.2" end From efd84b9709bc96d104dc6a6b119b2e57c9c92edf Mon Sep 17 00:00:00 2001 From: "shigeru.nakajima" Date: Tue, 12 Mar 2019 23:18:45 +0900 Subject: [PATCH 35/61] Ignore runs of phonetic that appears in Japanese (#77) --- lib/creek/shared_strings.rb | 2 +- spec/fixtures/sst.xml | 10 ++++++++++ spec/shared_string_spec.rb | 3 ++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/creek/shared_strings.rb b/lib/creek/shared_strings.rb index 6abd2e3..dd5c922 100644 --- a/lib/creek/shared_strings.rb +++ b/lib/creek/shared_strings.rb @@ -29,7 +29,7 @@ def self.parse_shared_string_from_document(xml) dictionary = Hash.new xml.css('si').each_with_index do |si, idx| - text_nodes = si.css('t') + text_nodes = si.css('>t, r t') if text_nodes.count == 1 # plain text node dictionary[idx] = Creek::Styles::Converter.unescape_string(text_nodes.first.content) else # rich text nodes with text fragments diff --git a/spec/fixtures/sst.xml b/spec/fixtures/sst.xml index 112db32..2b54051 100644 --- a/spec/fixtures/sst.xml +++ b/spec/fixtures/sst.xml @@ -78,4 +78,14 @@ Cell with_x000D_escaped_x000D_characters + + 吉田兼好 + + ヨシダ + + + ケンコウ + + + \ No newline at end of file diff --git a/spec/shared_string_spec.rb b/spec/shared_string_spec.rb index 11fbf20..5039898 100644 --- a/spec/shared_string_spec.rb +++ b/spec/shared_string_spec.rb @@ -7,13 +7,14 @@ doc = Nokogiri::XML(shared_strings_xml_file) dictionary = Creek::SharedStrings.parse_shared_string_from_document(doc) - expect(dictionary.keys.size).to eq(6) + expect(dictionary.keys.size).to eq(7) expect(dictionary[0]).to eq('Cell A1') expect(dictionary[1]).to eq('Cell B1') expect(dictionary[2]).to eq('My Cell') expect(dictionary[3]).to eq('Cell A2') expect(dictionary[4]).to eq('Cell B2') expect(dictionary[5]).to eq("Cell with\rescaped\rcharacters") + expect(dictionary[6]).to eq('吉田兼好') end end \ No newline at end of file From fe8d73a6a2889e8201c8c44d29c85081d85bdecd Mon Sep 17 00:00:00 2001 From: Ryan Bigg Date: Wed, 13 Mar 2019 01:21:28 +1100 Subject: [PATCH 36/61] Bump http version to 4.0 (#78) --- creek.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/creek.gemspec b/creek.gemspec index 07d44f9..7ad309f 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -27,5 +27,5 @@ Gem::Specification.new do |spec| spec.add_dependency 'nokogiri', '>= 1.7.0' spec.add_dependency 'rubyzip', '>= 1.0.0' - spec.add_dependency 'http', '~> 3.0' + spec.add_dependency 'http', '~> 4.0' end From 31e849902cd00e61a00c9abc78ddd50d5e5cfe1e Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Tue, 12 Mar 2019 10:25:53 -0400 Subject: [PATCH 37/61] bump up gem version. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 5f06a37..9aa813d 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.4.2" + VERSION = "2.4.3" end From 1a5c99032ee736e8f24ebd524adffba068d236b4 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Tue, 12 Mar 2019 10:27:08 -0400 Subject: [PATCH 38/61] bump up gem version. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 9aa813d..6d35778 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.4.3" + VERSION = "2.4.4" end From 6dad61b5b36dca6453e6d649499c73de9b3e92dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammad=20H=C3=A4rter?= Date: Mon, 30 Dec 2019 23:44:47 +0100 Subject: [PATCH 39/61] nil case handling (#79) --- lib/creek/drawing.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/creek/drawing.rb b/lib/creek/drawing.rb index f695979..d8735c3 100644 --- a/lib/creek/drawing.rb +++ b/lib/creek/drawing.rb @@ -83,7 +83,9 @@ def load_images_pathnames_by_cells col_to_selector = 'xdr:to/xdr:col'.freeze @drawings.xpath('//xdr:twoCellAnchor').each do |drawing| - embed = drawing.xpath(image_selector).first.attributes['embed'] + # embed = drawing.xpath(image_selector).first.attributes['embed'] + temp = drawing.xpath(image_selector).first + embed = temp.attributes['embed'] if temp next if embed.nil? rid = embed.value From f787567595147184b3c6ffb05e9c01dd60fa2868 Mon Sep 17 00:00:00 2001 From: David Bernheisel Date: Mon, 30 Dec 2019 17:53:18 -0500 Subject: [PATCH 40/61] Remove http requirement (#87) --- README.md | 6 ++++++ creek.gemspec | 3 +-- lib/creek/book.rb | 25 +++++++++++++++++-------- spec/spec_helper.rb | 4 ++++ spec/test_spec.rb | 14 ++++++++++++++ 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ea287db..b64d4b5 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,12 @@ Once this is complete, you should be able to run the test suite: rake ``` +There are some remote tests that are excluded by default. To run those, run + +``` +bundle exec rspec --tag remote +``` + ## Bug Reporting Please use the [Issues](https://github.com/pythonicrubyist/creek/issues) page to report bugs or suggest new enhancements. diff --git a/creek.gemspec b/creek.gemspec index 7ad309f..e261388 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -23,9 +23,8 @@ Gem::Specification.new do |spec| spec.add_development_dependency "bundler", "~> 1.3" spec.add_development_dependency "rake" spec.add_development_dependency 'rspec', '~> 3.6.0' - spec.add_development_dependency 'pry' + spec.add_development_dependency 'pry-byebug' spec.add_dependency 'nokogiri', '>= 1.7.0' spec.add_dependency 'rubyzip', '>= 1.0.0' - spec.add_dependency 'http', '~> 4.0' end diff --git a/lib/creek/book.rb b/lib/creek/book.rb index 613cac6..061b935 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -1,7 +1,7 @@ require 'zip/filesystem' require 'nokogiri' require 'date' -require 'http' +require 'open-uri' module Creek @@ -20,13 +20,7 @@ def initialize path, options = {} extension = File.extname(options[:original_filename] || path).downcase raise 'Not a valid file format.' unless (['.xlsx', '.xlsm'].include? extension) end - if options[:remote] - zipfile = Tempfile.new("file") - zipfile.binmode - zipfile.write(HTTP.get(path).to_s) - zipfile.close - path = zipfile.path - end + path = download_file(path) if options[:remote] @files = Zip::File.open(path) @shared_strings = SharedStrings.new(self) end @@ -79,5 +73,20 @@ def base_date result end end + + private + + def download_file(url) + # OpenUri will return a StringIO if under OpenURI::Buffer::StringMax + # threshold, and a Tempfile if over. + downloaded = URI(url).open + if downloaded.is_a? StringIO + path = Tempfile.new(['creek-file', '.xlsx']).path + File.binwrite(path, downloaded.read) + path + else + downloaded.path + end + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 13f918c..ed04310 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,7 @@ require 'creek' require 'pry' +require 'time' +RSpec.configure do |config| + config.filter_run_excluding remote: true +end diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 2ba1ebb..dabb932 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -106,6 +106,20 @@ expect(@creek).not_to be_nil end + it 'opens small remote files successfully', remote: true do + url = 'https://file-examples.com/wp-content/uploads/2017/02/file_example_XLSX_10.xlsx' + @creek = Creek::Book.new(url, remote: true) + + expect(@creek.sheets[0]).to be_a Creek::Sheet + end + + it 'opens large remote files successfully', remote: true do + url = 'http://www.house.leg.state.mn.us/comm/docs/BanaianZooExample.xlsx' + @creek = Creek::Book.new(url, remote: true) + + expect(@creek.sheets[0]).to be_a Creek::Sheet + end + it 'find sheets successfully.' do expect(@creek.sheets.count).to eq(1) sheet = @creek.sheets.first From c182b8af2e2aa237c351d8ac684056db1ffc7473 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Mon, 30 Dec 2019 18:20:17 -0500 Subject: [PATCH 41/61] [ creek ] upgrade bundler and nokogiri versions. --- creek.gemspec | 4 ++-- lib/creek/version.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/creek.gemspec b/creek.gemspec index e261388..834f464 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -20,11 +20,11 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 2.0.0' - spec.add_development_dependency "bundler", "~> 1.3" + spec.add_development_dependency "bundler", "~> 2.1.2" spec.add_development_dependency "rake" spec.add_development_dependency 'rspec', '~> 3.6.0' spec.add_development_dependency 'pry-byebug' - spec.add_dependency 'nokogiri', '>= 1.7.0' + spec.add_dependency 'nokogiri', '>= 1.10.0' spec.add_dependency 'rubyzip', '>= 1.0.0' end diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 6d35778..57d7c9c 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.4.4" + VERSION = "2.5.0" end From 544971296f460ae82cd5f5842aa1e6cec5a41bf7 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Sun, 8 Mar 2020 17:48:14 -0400 Subject: [PATCH 42/61] [ creek ] #88 fixed. (#91) --- lib/creek/styles/converter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/styles/converter.rb b/lib/creek/styles/converter.rb index 391861a..6a5acff 100644 --- a/lib/creek/styles/converter.rb +++ b/lib/creek/styles/converter.rb @@ -6,7 +6,7 @@ class Converter include Creek::Styles::Constants # Excel non-printable character escape sequence - HEX_ESCAPE_REGEXP = /_x[0-9A-Za-z]{4}_/ + HEX_ESCAPE_REGEXP = /_x[0-9A-Fa-f]{4}_/ ## # The heart of typecasting. The ruby type is determined either explicitly From e0d565750edd8c7ef464861a5e87d6c641e74061 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Sun, 8 Mar 2020 18:32:18 -0400 Subject: [PATCH 43/61] [ creek ] #88 gem version bumped up. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 57d7c9c..b8a9c96 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.5.0" + VERSION = "2.5.1" end From a3d887acb72a3f4adda7ae245cb7082d3c6bb221 Mon Sep 17 00:00:00 2001 From: Dmitry Alekseichik Date: Tue, 21 Apr 2020 16:50:10 +0300 Subject: [PATCH 44/61] Added mapping cell ids with header column name (#93) * Added mapping cell ids with header column name * Added readme description * Exclude masOS files in .gitignore * Added tests and small refactoring --- .gitignore | 3 ++ README.md | 8 ++++ lib/creek.rb | 2 + lib/creek/book.rb | 18 +++++++-- lib/creek/sheet.rb | 50 +++++++++++++++---------- spec/fixtures/sample-with-headers.xlsx | Bin 0 -> 9312 bytes spec/sheet_spec.rb | 37 +++++++++++++++--- 7 files changed, 89 insertions(+), 29 deletions(-) create mode 100644 spec/fixtures/sample-with-headers.xlsx diff --git a/.gitignore b/.gitignore index d87d4be..650a897 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ spec/reports test/tmp test/version_tmp tmp + +# Mac finder artifacts +.DS_Store diff --git a/README.md b/README.md index b64d4b5..ef2f414 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,14 @@ remote_url = 'http://dev-builds.libreoffice.org/tmp/test.xlsx' Creek::Book.new remote_url, remote: true ``` +## Mapping cells with header names +By default, Creek will map cell names with letter and number(A1, B3 and etc). To be able to get cell values by header column name use ***with_headers*** (can be used only with ***#simple_rows*** method!!!) during creation *(Note: header column is first string of sheet)* + +```ruby +creek = Creek::Book.new file.path, with_headers: true +``` + + ## Contributing Contributions are welcomed. You can fork a repository, add your code changes to the forked branch, ensure all existing unit tests pass, create new unit tests which cover your new changes and finally create a pull request. diff --git a/lib/creek.rb b/lib/creek.rb index 47201b9..318d1f6 100644 --- a/lib/creek.rb +++ b/lib/creek.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'creek/version' require 'creek/book' require 'creek/styles/constants' diff --git a/lib/creek/book.rb b/lib/creek/book.rb index 061b935..fae6090 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -4,12 +4,11 @@ require 'open-uri' module Creek - class Creek::Book - attr_reader :files, :sheets, - :shared_strings + :shared_strings, + :with_headers DATE_1900 = Date.new(1899, 12, 30).freeze DATE_1904 = Date.new(1904, 1, 1).freeze @@ -23,6 +22,7 @@ def initialize path, options = {} path = download_file(path) if options[:remote] @files = Zip::File.open(path) @shared_strings = SharedStrings.new(self) + @with_headers = options.fetch(:with_headers, false) end def sheets @@ -41,7 +41,17 @@ def sheets rels = Nokogiri::XML::Document.parse(rels_doc).css("Relationship") @sheets = xml.css(cssPrefix+'sheet').map do |sheet| sheetfile = rels.find { |el| sheet.attr("r:id") == el.attr("Id") }.attr("Target") - Sheet.new(self, sheet.attr("name"), sheet.attr("sheetid"), sheet.attr("state"), sheet.attr("visible"), sheet.attr("r:id"), sheetfile) + sheet = Sheet.new( + self, + sheet.attr("name"), + sheet.attr("sheetid"), + sheet.attr("state"), + sheet.attr("visible"), + sheet.attr("r:id"), + sheetfile + ) + sheet.with_headers = with_headers + sheet end end diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index 1871206..0c48fed 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'zip/filesystem' require 'nokogiri' @@ -5,16 +7,19 @@ module Creek class Creek::Sheet include Creek::Utils + HEADERS_ROW_NUMBER = '1' + + attr_accessor :with_headers attr_reader :book, :name, :sheetid, :state, :visible, :rid, - :index - + :index, + :headers - def initialize book, name, sheetid, state, visible, rid, sheetfile + def initialize(book, name, sheetid, state, visible, rid, sheetfile) @book = book @name = name @sheetid = sheetid @@ -46,7 +51,6 @@ def images_at(cell) @drawing.images_at(cell) if @images_present end - ## # Provides an Enumerator that returns a hash representing each row. # The key of the hash is the column ID and the value is the value of the cell. @@ -89,35 +93,37 @@ def rows_generator include_meta_data=false, use_simple_rows_format=false closer = Nokogiri::XML::Reader::TYPE_END_ELEMENT Enumerator.new do |y| row, cells, cell = nil, {}, nil - cell_type = nil + cell_type = nil cell_style_idx = nil @book.files.file.open(path) do |xml| Nokogiri::XML::Reader.from_io(xml).each do |node| - if (node.name.eql? 'row') and (node.node_type.eql? opener) + if node.name == 'row' && node.node_type == opener row = node.attributes - row['cells'] = Hash.new - cells = Hash.new + row['cells'] = {} + cells = {} y << (include_meta_data ? row : cells) if node.self_closing? - elsif (node.name.eql? 'row') and (node.node_type.eql? closer) + elsif node.name == 'row' && node.node_type == closer processed_cells = fill_in_empty_cells(cells, row['r'], cell, use_simple_rows_format) + @headers = processed_cells if row['r'] == HEADERS_ROW_NUMBER if @images_present processed_cells.each do |cell_name, cell_value| next unless cell_value.nil? + processed_cells[cell_name] = images_at(cell_name) end end row['cells'] = processed_cells y << (include_meta_data ? row : processed_cells) - elsif (node.name.eql? 'c') and (node.node_type.eql? opener) + elsif node.name == 'c' && node.node_type == opener cell_type = node.attributes['t'] cell_style_idx = node.attributes['s'] cell = node.attributes['r'] - elsif (['v', 't'].include? node.name) and (node.node_type.eql? opener) + elsif %w[v t].include?(node.name) && node.node_type == opener unless cell.nil? node.read - cells[(use_simple_rows_format ? cell.tr("0-9", "") : cell)] = convert(node.value, cell_type, cell_style_idx) + cells[cell] = convert(node.value, cell_type, cell_style_idx) end end end @@ -142,15 +148,13 @@ def converter_options # The unzipped XML file does not contain any node for empty cells. # Empty cells are being padded in using this function def fill_in_empty_cells(cells, row_number, last_col, use_simple_rows_format) - new_cells = Hash.new + new_cells = {} + return new_cells if cells.empty? - unless cells.empty? - last_col = last_col.gsub(row_number, '') - - ("A"..last_col).to_a.each do |column| - id = use_simple_rows_format ? "#{column}" : "#{column}#{row_number}" - new_cells[id] = cells[id] - end + last_col = last_col.gsub(row_number, '') + ('A'..last_col).to_a.each do |column| + id = cell_id(column, use_simple_rows_format, row_number) + new_cells[id] = cells["#{column}#{row_number}"] end new_cells @@ -172,5 +176,11 @@ def extract_drawing_filepath sheet_rels_filepath = expand_to_rels_path(sheet_filepath) parse_xml(sheet_rels_filepath).css("Relationship[@Id='#{drawing_rid}']").first.attributes['Target'].value end + + def cell_id(column, use_simple_rows_format, row_number = '') + return "#{column}#{row_number}" unless use_simple_rows_format + + with_headers && headers ? headers[column] : column + end end end diff --git a/spec/fixtures/sample-with-headers.xlsx b/spec/fixtures/sample-with-headers.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..966bc2de1863eea5c08062b7fe49092015ce3658 GIT binary patch literal 9312 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

_rA=w+1y zBh5a+LA87x(W`eg<(M)mu8qw!(zNXxK|-pOcchLz)V-5EJAvVHCjLEv@9kwXArs{|inBfkwVDXIS zkC<&;rxtPxYKas>zS4xa6r8GF46y<4;DYm?auf+fK&S#Q`R`!^0HpsS+|PY57-((n z2Il%>_(PLsr;Ryn2@*nnpj=@il9&i2>@2Bo8c{R0O|XliCE8(($EMn4y=E+Dw6-nX zYyS}+X_ufH>=iAW+6_L1eUa;67w?$YefQW{^Fu##8)ZQdKhyI$8;T-{u5A(_W?XzB z2jf~u{~iV2Uf;==JYv+ryn$F;vLI;&i-x4SobUo>&*H(&#Kz*_VaRuKm5Y()0-#8* z!>y*ttF0Lz@rQK5GW~Se58%5ANGhYXwSJQPIeoT{K}cY2QI&A{!jA8@{divcd7+g% z20ok7vrDz8!hFszc}-YbUb<49W`z$qLrJg$Eibx7iHlnG;kA)TEz(fHUX5 z72GuWk(&ErUqphT+1)Ru<=yZEdap<0?{C%{k1j#Yg+p#@2 zk-ynJuc1U>XpvZP4x4CV1})6b@vWBoja6ablN56f8^^cjyY%V< zO2Zl2NuZUV_YR%1a}3o|oOS2^X2tUDOyY~K&Lt0d9z+x8G^%6A>rfu7#$j>15zP9$PJc6k-8jZ*Fc{*#ecmvmX5w*IIdEzE6-J zT)D|Jt0E~OvNuk;noYi+lI_EJrnsB%iD4jqX*WBiH(mRaef=;~go0L_74^tdEzAhV zNkg`1kpV|ArxJZyG4+U8qHi%N{z`Ve4=FfuW!E^dhu87Tum5Y3f!9I^z1b4~hLIL6 z;(Qy-k-n<_7q*gf_4=!`_R`Gbs*nl(YB7R*4$6n;@tk<1G>*`RO|HeVoSFiV8IG{s_#t?tA?@FMUk5voy#3b zlkQ27VWUuj1QUve7N(nsfN-SOnHkEJvGguO!msAy#83Ontc_h&nb1Y0sD&LRpBy$9 zaPQGkxTv?rNlhF(Ser$?BIF;4_>>qa<}l#5bz}oE6rDl=4)B@IccKW8{z+loa3Zetll*UID|AHhnMh zV@O13gygy@!sA8Ge7U5Zv?g+-1KmQYs4wiqp%LHgX8nvz6Gau<~K2p0Nb!}7mVD8yJSbfwPX={ zuL=ERGd))@0lhr@(;+9=Z9C4-qedYuBFStcRUU2UGcqPB*fYz)6W^UJB^=Bj zUkkB}d?u}F{Vanu%E668kKVgy8e|1H37nlEeB7DjI zePkz2IIVEwhCzR70VvN-0~&g$g(f41D^L?jZx)tnWGKt5_1OuzUBgUE8q|9Dw)vI} zQv&;~6HX_ae6&?)O}~$BBx2{on6qnTu?4dl@rV#oKO|1C%6>ehPv3q(=LI1M49|<{ z4ZX@`hr~d|jC1BV4ayFAf{w4By4Y`-LYf*MKtsI`XX-mR$0#CD<=0<;1+p;*4qC`A z1J$x#@X|b89HK*T&_#Wqze4T)cDwaiUU;t|QnphKO;tp&^wAr}jzMlxGn=+?0 z>v!mMhy2*kK@(_UGN`ZJt-+}mkJX#H3^j~$pBThgCBGI86z&f>s3AOPWDYj8So*gqvcV*6wA5!E+DA^%V}; zJJjz!N@6_Y9ek6VunAkv%Tl&CNq5J0Hn$j0vD{lw2b&K#!|fNP`X3GWkDl{u;;=OB z)s2{-2EVN*0Fe^X~}17is@O0K#{r@W=mNvi%+4_kHYN01$Ys8x8>ezg_L`roSul zzf6CS{NL37q0fIu`FmRZ3j>Zo2>|>f$^LHs_fYt&ITFn;=Kl_inkp#pw+jHE!+$~W LJU&YI^XmTrfk`3% literal 0 HcmV?d00001 diff --git a/spec/sheet_spec.rb b/spec/sheet_spec.rb index 4d2ef14..859a91b 100644 --- a/spec/sheet_spec.rb +++ b/spec/sheet_spec.rb @@ -1,26 +1,26 @@ +# frozen_string_literal: true + require './spec/spec_helper' describe 'sheet' do let(:book_with_images) { Creek::Book.new('spec/fixtures/sample-with-images.xlsx') } - let(:book_no_images) { Creek::Book.new('spec/fixtures/sample.xlsx') } let(:sheetfile) { 'worksheets/sheet1.xml' } let(:sheet_with_images) { Creek::Sheet.new(book_with_images, 'Sheet 1', 1, '', '', '1', sheetfile) } - let(:sheet_no_images) { Creek::Sheet.new(book_no_images, 'Sheet 1', 1, '', '', '1', sheetfile) } def load_cell(rows, cell_name) - cell = rows.find { |row| !row[cell_name].nil? } + cell = rows.find { |row| row[cell_name] } cell[cell_name] if cell end context 'escaped ampersand' do let(:book_escaped) { Creek::Book.new('spec/fixtures/escaped.xlsx') } it 'does NOT escape ampersand' do - expect(book_escaped.sheets[0].rows.to_enum.map(&:values)).to eq([["abc", "def"], ["ghi", "j&k"]]) + expect(book_escaped.sheets[0].rows.to_enum.map(&:values)).to eq([%w[abc def], %w[ghi j&k]]) end let(:book_escaped2) { Creek::Book.new('spec/fixtures/escaped2.xlsx') } it 'does escape ampersand' do - expect(book_escaped2.sheets[0].rows.to_enum.map(&:values)).to eq([["abc", "def"], ["ghi", "j&k"]]) + expect(book_escaped2.sheets[0].rows.to_enum.map(&:values)).to eq([%w[abc def], %w[ghi j&k]]) end end @@ -66,6 +66,9 @@ def load_cell(rows, cell_name) end context 'with excel without images' do + let(:book_no_images) { Creek::Book.new('spec/fixtures/sample.xlsx') } + let(:sheet_no_images) { Creek::Sheet.new(book_no_images, 'Sheet 1', 1, '', '', '1', sheetfile) } + it 'does not break on with_images' do rows = sheet_no_images.with_images.rows.map { |r| r } expect(load_cell(rows, 'A10')).to eq(0.15) @@ -94,4 +97,28 @@ def load_cell(rows, cell_name) expect(image).to eq(nil) end end + + describe '#simple_rows' do + let(:book_with_headers) { Creek::Book.new('spec/fixtures/sample-with-headers.xlsx') } + let(:sheet) { Creek::Sheet.new(book_with_headers, 'Sheet 1', 1, '', '', '1', sheetfile) } + + subject { sheet.simple_rows.to_a[1] } + + it 'returns values by letters' do + expect(subject['A']).to eq 'value1' + expect(subject['B']).to eq 'value2' + end + + context 'when enable with_headers property' do + before { sheet.with_headers = true } + + subject { sheet.simple_rows.to_a[1] } + + it 'returns values by headers name' do + expect(subject['HeaderA']).to eq 'value1' + expect(subject['HeaderB']).to eq 'value2' + expect(subject['HeaderC']).to eq 'value3' + end + end + end end From 7a022e89caee3d428890c0460d75ccbc8b04f284 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Tue, 21 Apr 2020 10:11:41 -0400 Subject: [PATCH 45/61] [ creek ] #93 gem version bumped up. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index b8a9c96..0618fb9 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.5.1" + VERSION = "2.5.2" end From f29af88aabac923a26a96dc34b5fa3067e7ad114 Mon Sep 17 00:00:00 2001 From: Michiel de Mare Date: Sun, 2 Aug 2020 16:00:40 +0200 Subject: [PATCH 46/61] Fix BigDecimal (#95) `BigDecimal.new` has been removed in ruby 2.7. Therefore converting big decimals is broken. Use `BigDecimal(value)` instead. https://ruby-doc.org/stdlib-2.7.1/libdoc/bigdecimal/rdoc/BigDecimal.html --- lib/creek/styles/converter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/styles/converter.rb b/lib/creek/styles/converter.rb index 6a5acff..afe1eb6 100644 --- a/lib/creek/styles/converter.rb +++ b/lib/creek/styles/converter.rb @@ -110,7 +110,7 @@ def self.convert_datetime(value, options) def self.convert_bignum(value) if defined?(BigDecimal) - BigDecimal.new(value) + BigDecimal(value) else value.to_f end From 327cf310519c2ac701dd65ecb0045ac1215522de Mon Sep 17 00:00:00 2001 From: Peter Postma Date: Mon, 12 Jul 2021 14:48:08 +0200 Subject: [PATCH 47/61] Reset headers each time when creating the enumerator. (#98) This fixes headers being messed up when #simple_rows is called multiple times. --- lib/creek/sheet.rb | 5 +++-- spec/sheet_spec.rb | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index 0c48fed..6f97df6 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -92,6 +92,7 @@ def rows_generator include_meta_data=false, use_simple_rows_format=false opener = Nokogiri::XML::Reader::TYPE_ELEMENT closer = Nokogiri::XML::Reader::TYPE_END_ELEMENT Enumerator.new do |y| + @headers = nil row, cells, cell = nil, {}, nil cell_type = nil cell_style_idx = nil @@ -104,7 +105,7 @@ def rows_generator include_meta_data=false, use_simple_rows_format=false y << (include_meta_data ? row : cells) if node.self_closing? elsif node.name == 'row' && node.node_type == closer processed_cells = fill_in_empty_cells(cells, row['r'], cell, use_simple_rows_format) - @headers = processed_cells if row['r'] == HEADERS_ROW_NUMBER + @headers = processed_cells if with_headers && row['r'] == HEADERS_ROW_NUMBER if @images_present processed_cells.each do |cell_name, cell_value| @@ -177,7 +178,7 @@ def extract_drawing_filepath parse_xml(sheet_rels_filepath).css("Relationship[@Id='#{drawing_rid}']").first.attributes['Target'].value end - def cell_id(column, use_simple_rows_format, row_number = '') + def cell_id(column, use_simple_rows_format, row_number) return "#{column}#{row_number}" unless use_simple_rows_format with_headers && headers ? headers[column] : column diff --git a/spec/sheet_spec.rb b/spec/sheet_spec.rb index 859a91b..11f7ca6 100644 --- a/spec/sheet_spec.rb +++ b/spec/sheet_spec.rb @@ -112,13 +112,23 @@ def load_cell(rows, cell_name) context 'when enable with_headers property' do before { sheet.with_headers = true } - subject { sheet.simple_rows.to_a[1] } - it 'returns values by headers name' do expect(subject['HeaderA']).to eq 'value1' expect(subject['HeaderB']).to eq 'value2' expect(subject['HeaderC']).to eq 'value3' end + + it 'returns headers correctly when called multiple times' do + row = sheet.simple_rows.to_a[1] + expect(row['HeaderA']).to eq 'value1' + expect(row['HeaderB']).to eq 'value2' + expect(row['HeaderC']).to eq 'value3' + + row = sheet.simple_rows.to_a[1] + expect(row['HeaderA']).to eq 'value1' + expect(row['HeaderB']).to eq 'value2' + expect(row['HeaderC']).to eq 'value3' + end end end end From 2ce2466abb318347447c941ddda561085f96e9e9 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Mon, 12 Jul 2021 08:50:11 -0400 Subject: [PATCH 48/61] [ creek ] #98 gem version bumped up. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 0618fb9..c397f24 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.5.2" + VERSION = "2.5.3" end From 494ed0505930ef2a70c26204bf8231228b6aa963 Mon Sep 17 00:00:00 2001 From: Ben Schmeckpeper Date: Tue, 29 Nov 2022 06:39:17 -0600 Subject: [PATCH 49/61] Handle XML namespaces in worksheets (#101) * Use local_name to ignore namespaces when enumerating a sheet's rows. * Include XML namespace when constructing the shared strings dictionary * Respect the configured namespace when parsing a sheet's rows Instead of using local_name, which throws away the namespace prefix, identify the configured namespace prefix (if there is one) and use that when looking for nodes in the SAX parsing loop. --- lib/creek/shared_strings.rb | 16 ++- lib/creek/sheet.rb | 18 +++- .../sample-with-headers_namespaced.xlsx | Bin 0 -> 8747 bytes spec/fixtures/sample_namespaced.xlsx | Bin 0 -> 8394 bytes spec/fixtures/sst_namespaced.xml | 91 ++++++++++++++++++ spec/shared_string_spec.rb | 19 +++- spec/sheet_spec.rb | 42 ++++++++ 7 files changed, 178 insertions(+), 8 deletions(-) create mode 100644 spec/fixtures/sample-with-headers_namespaced.xlsx create mode 100644 spec/fixtures/sample_namespaced.xlsx create mode 100644 spec/fixtures/sst_namespaced.xml diff --git a/lib/creek/shared_strings.rb b/lib/creek/shared_strings.rb index dd5c922..1825101 100644 --- a/lib/creek/shared_strings.rb +++ b/lib/creek/shared_strings.rb @@ -5,6 +5,8 @@ module Creek class Creek::SharedStrings + SPREADSHEETML_URI = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' + attr_reader :book, :dictionary def initialize book @@ -27,9 +29,17 @@ def parse_shared_string_from_document(xml) def self.parse_shared_string_from_document(xml) dictionary = Hash.new - - xml.css('si').each_with_index do |si, idx| - text_nodes = si.css('>t, r t') + namespace = xml.namespaces.detect{|_key, uri| uri == SPREADSHEETML_URI } + prefix = if namespace && namespace[0].start_with?('xmlns:') + namespace[0].delete_prefix('xmlns:') + '|' + else + '' + end + node_selector = "#{prefix}si" + text_selector = ">#{prefix}t, #{prefix}r #{prefix}t" + + xml.css(node_selector).each_with_index do |si, idx| + text_nodes = si.css(text_selector) if text_nodes.count == 1 # plain text node dictionary[idx] = Creek::Styles::Converter.unescape_string(text_nodes.first.content) else # rich text nodes with text fragments diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index 6f97df6..be30c51 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -8,6 +8,7 @@ class Creek::Sheet include Creek::Utils HEADERS_ROW_NUMBER = '1' + SPREADSHEETML_URI = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' attr_accessor :with_headers attr_reader :book, @@ -97,13 +98,22 @@ def rows_generator include_meta_data=false, use_simple_rows_format=false cell_type = nil cell_style_idx = nil @book.files.file.open(path) do |xml| + prefix = '' Nokogiri::XML::Reader.from_io(xml).each do |node| - if node.name == 'row' && node.node_type == opener + if prefix.empty? && node.namespaces.any? + namespace = node.namespaces.detect{|_key, uri| uri == SPREADSHEETML_URI } + prefix = if namespace && namespace[0].start_with?('xmlns:') + namespace[0].delete_prefix('xmlns:') + ':' + else + '' + end + end + if node.name == "#{prefix}row" && node.node_type == opener row = node.attributes row['cells'] = {} cells = {} y << (include_meta_data ? row : cells) if node.self_closing? - elsif node.name == 'row' && node.node_type == closer + elsif node.name == "#{prefix}row" && node.node_type == closer processed_cells = fill_in_empty_cells(cells, row['r'], cell, use_simple_rows_format) @headers = processed_cells if with_headers && row['r'] == HEADERS_ROW_NUMBER @@ -117,11 +127,11 @@ def rows_generator include_meta_data=false, use_simple_rows_format=false row['cells'] = processed_cells y << (include_meta_data ? row : processed_cells) - elsif node.name == 'c' && node.node_type == opener + elsif node.name == "#{prefix}c" && node.node_type == opener cell_type = node.attributes['t'] cell_style_idx = node.attributes['s'] cell = node.attributes['r'] - elsif %w[v t].include?(node.name) && node.node_type == opener + elsif ["#{prefix}v", "#{prefix}t"].include?(node.name) && node.node_type == opener unless cell.nil? node.read cells[cell] = convert(node.value, cell_type, cell_style_idx) diff --git a/spec/fixtures/sample-with-headers_namespaced.xlsx b/spec/fixtures/sample-with-headers_namespaced.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d86f96a88429f078d732b40ca629ad9714751f8a GIT binary patch literal 8747 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 Wg@Yu{*Y5~m0@5ML?MD3dxBmbntPL~( literal 0 HcmV?d00001 diff --git a/spec/fixtures/sample_namespaced.xlsx b/spec/fixtures/sample_namespaced.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a2f4a40cd7151826120f3aa336c28826ef8ca702 GIT binary patch literal 8394 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 literal 0 HcmV?d00001 diff --git a/spec/fixtures/sst_namespaced.xml b/spec/fixtures/sst_namespaced.xml new file mode 100644 index 0000000..cabf0ed --- /dev/null +++ b/spec/fixtures/sst_namespaced.xml @@ -0,0 +1,91 @@ + + + + Cell A1 + + + Cell B1 + + + My Cell + + + + + + + + + + + Cell + + + + + + + + + + + + + + + + + + + + + A2 + + + + + + + + + + + + Cell + + + + + + + + + + + + + + + + + + + + + B2 + + + + Cell with_x000D_escaped_x000D_characters + + + 吉田兼好 + + ヨシダ + + + ケンコウ + + + + diff --git a/spec/shared_string_spec.rb b/spec/shared_string_spec.rb index 5039898..11b0130 100644 --- a/spec/shared_string_spec.rb +++ b/spec/shared_string_spec.rb @@ -17,4 +17,21 @@ expect(dictionary[6]).to eq('吉田兼好') end -end \ No newline at end of file + context 'when the nodes are namespaced' do + it 'parses the dictionary correctly' do + shared_strings_xml_file = File.open('spec/fixtures/sst_namespaced.xml') + doc = Nokogiri::XML(shared_strings_xml_file) + dictionary = Creek::SharedStrings.parse_shared_string_from_document(doc) + + expect(dictionary.keys.size).to eq(7) + expect(dictionary[0]).to eq('Cell A1') + expect(dictionary[1]).to eq('Cell B1') + expect(dictionary[2]).to eq('My Cell') + expect(dictionary[3]).to eq('Cell A2') + expect(dictionary[4]).to eq('Cell B2') + expect(dictionary[5]).to eq("Cell with\rescaped\rcharacters") + expect(dictionary[6]).to eq('吉田兼好') + end + + end +end diff --git a/spec/sheet_spec.rb b/spec/sheet_spec.rb index 11f7ca6..5a5c56a 100644 --- a/spec/sheet_spec.rb +++ b/spec/sheet_spec.rb @@ -74,6 +74,16 @@ def load_cell(rows, cell_name) expect(load_cell(rows, 'A10')).to eq(0.15) end end + + context 'when nodes are namespaced' do + let(:namespaced_book) { Creek::Book.new('spec/fixtures/sample_namespaced.xlsx') } + let(:namespaced_sheet) { Creek::Sheet.new(namespaced_book, 'Sheet 1', 1, '', '', '1', sheetfile) } + + it 'parses rows correctly' do + rows = namespaced_sheet.rows.map { |r| r } + expect(load_cell(rows, 'A10')).to eq(0.15) + end + end end describe '#images_at' do @@ -130,5 +140,37 @@ def load_cell(rows, cell_name) expect(row['HeaderC']).to eq 'value3' end end + + context 'when nodes are namespaced' do + let(:namespaced_book) { Creek::Book.new('spec/fixtures/sample-with-headers_namespaced.xlsx') } + let(:sheet) { Creek::Sheet.new(namespaced_book, 'Sheet 1', 1, '', '', '1', sheetfile) } + + it 'returns values by letters' do + expect(subject['A']).to eq 'value1' + expect(subject['B']).to eq 'value2' + end + + context 'when enable with_headers property' do + before { sheet.with_headers = true } + + it 'returns values by headers name' do + expect(subject['HeaderA']).to eq 'value1' + expect(subject['HeaderB']).to eq 'value2' + expect(subject['HeaderC']).to eq 'value3' + end + + it 'returns headers correctly when called multiple times' do + row = sheet.simple_rows.to_a[1] + expect(row['HeaderA']).to eq 'value1' + expect(row['HeaderB']).to eq 'value2' + expect(row['HeaderC']).to eq 'value3' + + row = sheet.simple_rows.to_a[1] + expect(row['HeaderA']).to eq 'value1' + expect(row['HeaderB']).to eq 'value2' + expect(row['HeaderC']).to eq 'value3' + end + end + end end end From 4c6b9fa61885b05a27baf54c41a7330ee6a6099f Mon Sep 17 00:00:00 2001 From: Pavit Kaur <43848110+PavitKaur05@users.noreply.github.com> Date: Tue, 29 Nov 2022 18:10:08 +0530 Subject: [PATCH 50/61] Support for parsing one cell anchored images (#107) --- lib/creek/drawing.rb | 19 +++++++++++------- spec/drawing_spec.rb | 18 +++++++++++++++++ .../sample-with-one-cell-anchored-images.xlsx | Bin 0 -> 249401 bytes spec/sheet_spec.rb | 14 +++++++++++++ 4 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 spec/fixtures/sample-with-one-cell-anchored-images.xlsx diff --git a/lib/creek/drawing.rb b/lib/creek/drawing.rb index d8735c3..efba81a 100644 --- a/lib/creek/drawing.rb +++ b/lib/creek/drawing.rb @@ -66,7 +66,7 @@ def tmpdir # Drawing xml contains relationships ID's and coordinates (row, col). # Drawing relationships xml contains images' locations. def load_drawings_and_rels - @drawings = parse_xml(@drawing_filepath).css('xdr|twoCellAnchor') + @drawings = parse_xml(@drawing_filepath).css('xdr|twoCellAnchor', 'xdr|oneCellAnchor' ) drawing_rels_filepath = expand_to_rels_path(@drawing_filepath) @drawings_rels = parse_xml(drawing_rels_filepath).css('Relationships') end @@ -82,7 +82,7 @@ def load_images_pathnames_by_cells col_from_selector = 'xdr:from/xdr:col'.freeze col_to_selector = 'xdr:to/xdr:col'.freeze - @drawings.xpath('//xdr:twoCellAnchor').each do |drawing| + @drawings.xpath('//xdr:twoCellAnchor', '//xdr:oneCellAnchor').each do |drawing| # embed = drawing.xpath(image_selector).first.attributes['embed'] temp = drawing.xpath(image_selector).first embed = temp.attributes['embed'] if temp @@ -93,12 +93,17 @@ def load_images_pathnames_by_cells row_from = drawing.xpath(row_from_selector).text.to_i col_from = drawing.xpath(col_from_selector).text.to_i - row_to = drawing.xpath(row_to_selector).text.to_i - col_to = drawing.xpath(col_to_selector).text.to_i - (col_from..col_to).each do |col| - (row_from..row_to).each do |row| - @images_pathnames[[row, col]].push(path) + if drawing.name == 'oneCellAnchor' + @images_pathnames[[row_from , col_from ]].push(path) + else + row_to = drawing.xpath(row_to_selector).text.to_i + col_to = drawing.xpath(col_to_selector).text.to_i + + (col_from..col_to).each do |col| + (row_from..row_to).each do |row| + @images_pathnames[[row, col]].push(path) + end end end end diff --git a/spec/drawing_spec.rb b/spec/drawing_spec.rb index 632e01b..705941f 100644 --- a/spec/drawing_spec.rb +++ b/spec/drawing_spec.rb @@ -3,9 +3,11 @@ describe 'drawing' do let(:book) { Creek::Book.new('spec/fixtures/sample-with-images.xlsx') } let(:book_no_images) { Creek::Book.new('spec/fixtures/sample.xlsx') } + let(:book_with_one_cell_anchored_images) { Creek::Book.new('spec/fixtures/sample-with-one-cell-anchored-images.xlsx') } let(:drawingfile) { 'xl/drawings/drawing1.xml' } let(:drawing) { Creek::Drawing.new(book, drawingfile) } let(:drawing_without_images) { Creek::Drawing.new(book_no_images, drawingfile) } + let(:drawing_with_one_cell_anchored_images) { Creek::Drawing.new(book_with_one_cell_anchored_images, drawingfile) } describe '#has_images?' do it 'has' do @@ -48,5 +50,21 @@ expect(image1).to eq(image2) end end + + context 'when one cell anchored images in cell' do + it 'returns image for anchored cell' do + image = drawing_with_one_cell_anchored_images.images_at('A2')[0] + expect(image.class).to eq(Pathname) + expect(image.exist?).to eq(true) + end + + it 'returns nil for non-anchored cell' do + image = drawing_with_one_cell_anchored_images.images_at('A3')[0] + # Image can be seen present on cell A4 in `sample-with-one-cell-anchored-images.xlsx` + image_at_non_anchored_cell = drawing_with_one_cell_anchored_images.images_at('A4') + expect(image.class).to eq(Pathname) + expect(image_at_non_anchored_cell).to eq(nil) + end + end end end diff --git a/spec/fixtures/sample-with-one-cell-anchored-images.xlsx b/spec/fixtures/sample-with-one-cell-anchored-images.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..4a2f95d1d0e8fbb4d5c5e41561ba39f6e7a71053 GIT binary patch literal 249401 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�O!>RL;) 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 Date: Tue, 29 Nov 2022 18:11:47 +0530 Subject: [PATCH 51/61] Run test using github actions (#109) --- .github/workflows/test.yml | 35 +++++++++++++++++++++++++++++++++++ creek.gemspec | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..348b6fd --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,35 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake +# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby + +name: Ruby + +on: + push: + branches: [ "*" ] + pull_request: + branches: [ "*" ] + +permissions: + contents: read + +jobs: + test: + + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: ['2.6', '2.7', '3.0', '3.1'] + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Run tests + run: bundle exec rake diff --git a/creek.gemspec b/creek.gemspec index 834f464..ef6bce5 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -20,7 +20,7 @@ Gem::Specification.new do |spec| spec.required_ruby_version = '>= 2.0.0' - spec.add_development_dependency "bundler", "~> 2.1.2" + spec.add_development_dependency "bundler" spec.add_development_dependency "rake" spec.add_development_dependency 'rspec', '~> 3.6.0' spec.add_development_dependency 'pry-byebug' From e032658e5cab1badb52b589d0720f01604d87a2b Mon Sep 17 00:00:00 2001 From: pythonicrubyist Date: Tue, 29 Nov 2022 10:11:20 -0500 Subject: [PATCH 52/61] [ creek ] #101 gem version bumped up. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index c397f24..40c2a5c 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.5.3" + VERSION = "2.6.1" end From 3b7e72d0113ba993d5746b327afcc337bd295425 Mon Sep 17 00:00:00 2001 From: pythonicrubyist Date: Tue, 29 Nov 2022 10:14:46 -0500 Subject: [PATCH 53/61] pythonicrubyist [ creek ] #102 gem version bumped up. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 40c2a5c..1d6ad28 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,3 @@ module Creek - VERSION = "2.6.1" + VERSION = "2.6.2" end From 2172e0059648cdb5e694d50ebc9160586a974640 Mon Sep 17 00:00:00 2001 From: Junil Jacob Date: Mon, 9 Jan 2023 20:20:53 +0530 Subject: [PATCH 54/61] Fix download badge (#112) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef2f414..28e43c7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![version](https://badge.fury.io/rb/creek.svg)](https://badge.fury.io/rb/creek) -[![downloads](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads)](https://ruby-gem-downloads-badge.herokuapp.com/creek?type=total&total_label=downloads) +[![downloads](https://img.shields.io/gem/dt/creek)](https://rubygems.org/gems/creek) # Creek - Stream parser for large Excel (xlsx and xlsm) files. From 9462de17bb67f594d4336a22769fc8bc2293e381 Mon Sep 17 00:00:00 2001 From: ThomasSevestre <1258170+ThomasSevestre@users.noreply.github.com> Date: Wed, 3 May 2023 21:25:59 +0200 Subject: [PATCH 55/61] Limit object allocations (#115) * add missing frozen_string_litteral * limit string allocations in Creek::Sheet#rows_generator --- lib/creek/book.rb | 2 ++ lib/creek/drawing.rb | 2 ++ lib/creek/shared_strings.rb | 2 ++ lib/creek/sheet.rb | 16 ++++++++++++---- lib/creek/styles.rb | 2 ++ lib/creek/styles/converter.rb | 2 ++ lib/creek/styles/style_types.rb | 2 ++ lib/creek/utils.rb | 2 ++ lib/creek/version.rb | 2 ++ 9 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/creek/book.rb b/lib/creek/book.rb index fae6090..1bb26e5 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'zip/filesystem' require 'nokogiri' require 'date' diff --git a/lib/creek/drawing.rb b/lib/creek/drawing.rb index efba81a..7847536 100644 --- a/lib/creek/drawing.rb +++ b/lib/creek/drawing.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'pathname' module Creek diff --git a/lib/creek/shared_strings.rb b/lib/creek/shared_strings.rb index 1825101..663aaf1 100644 --- a/lib/creek/shared_strings.rb +++ b/lib/creek/shared_strings.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'zip/filesystem' require 'nokogiri' diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index be30c51..1df94b8 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -99,6 +99,10 @@ def rows_generator include_meta_data=false, use_simple_rows_format=false cell_style_idx = nil @book.files.file.open(path) do |xml| prefix = '' + name_row = "row" + name_c = "c" + name_v = "v" + name_t = "t" Nokogiri::XML::Reader.from_io(xml).each do |node| if prefix.empty? && node.namespaces.any? namespace = node.namespaces.detect{|_key, uri| uri == SPREADSHEETML_URI } @@ -107,13 +111,17 @@ def rows_generator include_meta_data=false, use_simple_rows_format=false else '' end + name_row = "#{prefix}row" + name_c = "#{prefix}c" + name_v = "#{prefix}v" + name_t = "#{prefix}t" end - if node.name == "#{prefix}row" && node.node_type == opener + if node.name == name_row && node.node_type == opener row = node.attributes row['cells'] = {} cells = {} y << (include_meta_data ? row : cells) if node.self_closing? - elsif node.name == "#{prefix}row" && node.node_type == closer + elsif node.name == name_row && node.node_type == closer processed_cells = fill_in_empty_cells(cells, row['r'], cell, use_simple_rows_format) @headers = processed_cells if with_headers && row['r'] == HEADERS_ROW_NUMBER @@ -127,11 +135,11 @@ def rows_generator include_meta_data=false, use_simple_rows_format=false row['cells'] = processed_cells y << (include_meta_data ? row : processed_cells) - elsif node.name == "#{prefix}c" && node.node_type == opener + elsif node.name == name_c && node.node_type == opener cell_type = node.attributes['t'] cell_style_idx = node.attributes['s'] cell = node.attributes['r'] - elsif ["#{prefix}v", "#{prefix}t"].include?(node.name) && node.node_type == opener + elsif (node.name == name_v || node.name == name_t) && node.node_type == opener unless cell.nil? node.read cells[cell] = convert(node.value, cell_type, cell_style_idx) diff --git a/lib/creek/styles.rb b/lib/creek/styles.rb index d4681e9..016f158 100644 --- a/lib/creek/styles.rb +++ b/lib/creek/styles.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Creek class Styles attr_accessor :book diff --git a/lib/creek/styles/converter.rb b/lib/creek/styles/converter.rb index afe1eb6..62bd6d6 100644 --- a/lib/creek/styles/converter.rb +++ b/lib/creek/styles/converter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'set' module Creek diff --git a/lib/creek/styles/style_types.rb b/lib/creek/styles/style_types.rb index de0db03..df3b4d1 100644 --- a/lib/creek/styles/style_types.rb +++ b/lib/creek/styles/style_types.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # https://github.com/hmcgowan/roo/blob/master/lib/roo/excelx.rb # https://github.com/woahdae/simple_xlsx_reader/blob/master/lib/simple_xlsx_reader.rb#L231 module Creek diff --git a/lib/creek/utils.rb b/lib/creek/utils.rb index 9eef47c..af4ac90 100644 --- a/lib/creek/utils.rb +++ b/lib/creek/utils.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Creek module Utils def expand_to_rels_path(filepath) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 1d6ad28..2073d94 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Creek VERSION = "2.6.2" end From a91109dcd5814edc98a6a474263f9a624e5adee1 Mon Sep 17 00:00:00 2001 From: Peter Goldstein Date: Wed, 3 May 2023 15:26:46 -0400 Subject: [PATCH 56/61] Add Ruby 3.2 to CI (#113) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 348b6fd..fffbe9d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.6', '2.7', '3.0', '3.1'] + ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2'] steps: - uses: actions/checkout@v3 From b6eeda1cdeaeb2c7e0955f48e8d1859c90716806 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Wed, 3 May 2023 15:51:58 -0400 Subject: [PATCH 57/61] pythonicrubyist [ creek ] #113 and #115 gem version bumped up. --- lib/creek/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 2073d94..7cb8f1e 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Creek - VERSION = "2.6.2" + VERSION = "2.6.3" end From c6527895a624c3581e8a17150f9bba830f25604b Mon Sep 17 00:00:00 2001 From: Nick Weiland Date: Mon, 3 Jul 2023 11:40:15 -0700 Subject: [PATCH 58/61] Removes unused attr_reader and memoizes Book#sheets (#114) --- lib/creek/book.rb | 49 ++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/creek/book.rb b/lib/creek/book.rb index 1bb26e5..8fff8e6 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -8,7 +8,6 @@ module Creek class Creek::Book attr_reader :files, - :sheets, :shared_strings, :with_headers @@ -28,32 +27,34 @@ def initialize path, options = {} end def sheets - doc = @files.file.open "xl/workbook.xml" - xml = Nokogiri::XML::Document.parse doc - namespaces = xml.namespaces + @sheets ||= begin + doc = @files.file.open "xl/workbook.xml" + xml = Nokogiri::XML::Document.parse doc + namespaces = xml.namespaces - cssPrefix = '' - namespaces.each do |namespace| - if namespace[1] == 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' && namespace[0] != 'xmlns' then - cssPrefix = namespace[0].split(':')[1]+'|' + cssPrefix = '' + namespaces.each do |namespace| + if namespace[1] == 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' && namespace[0] != 'xmlns' then + cssPrefix = namespace[0].split(':')[1]+'|' + end end - end - rels_doc = @files.file.open "xl/_rels/workbook.xml.rels" - rels = Nokogiri::XML::Document.parse(rels_doc).css("Relationship") - @sheets = xml.css(cssPrefix+'sheet').map do |sheet| - sheetfile = rels.find { |el| sheet.attr("r:id") == el.attr("Id") }.attr("Target") - sheet = Sheet.new( - self, - sheet.attr("name"), - sheet.attr("sheetid"), - sheet.attr("state"), - sheet.attr("visible"), - sheet.attr("r:id"), - sheetfile - ) - sheet.with_headers = with_headers - sheet + rels_doc = @files.file.open "xl/_rels/workbook.xml.rels" + rels = Nokogiri::XML::Document.parse(rels_doc).css("Relationship") + xml.css(cssPrefix+'sheet').map do |sheet| + sheetfile = rels.find { |el| sheet.attr("r:id") == el.attr("Id") }.attr("Target") + sheet = Sheet.new( + self, + sheet.attr("name"), + sheet.attr("sheetid"), + sheet.attr("state"), + sheet.attr("visible"), + sheet.attr("r:id"), + sheetfile + ) + sheet.with_headers = with_headers + sheet + end end end From 67b5cd144b5db69cfe717a4440868007d295ce39 Mon Sep 17 00:00:00 2001 From: Ramy Hammam <56901837+ramy523@users.noreply.github.com> Date: Mon, 3 Jul 2023 14:43:12 -0400 Subject: [PATCH 59/61] Delete .DS_Store --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Mon, 18 Mar 2024 22:29:02 +0100 Subject: [PATCH 60/61] Add linter (#119) * added rubocop * applied lint * added lint check in ci --- .github/workflows/test.yml | 15 ++++ .gitignore | 2 + .rubocop.yml | 27 ++++++++ Rakefile | 4 +- creek.gemspec | 28 ++++---- lib/creek/book.rb | 58 ++++++++-------- lib/creek/drawing.rb | 36 +++++----- lib/creek/shared_strings.rb | 31 ++++----- lib/creek/sheet.rb | 118 ++++++++++++++++---------------- lib/creek/styles.rb | 17 ++--- lib/creek/styles/constants.rb | 22 +++--- lib/creek/styles/converter.rb | 48 ++++++------- lib/creek/styles/style_types.rb | 28 ++++---- lib/creek/utils.rb | 2 +- lib/creek/version.rb | 2 +- spec/drawing_spec.rb | 4 +- spec/shared_string_spec.rb | 2 - spec/sheet_spec.rb | 8 ++- spec/styles/converter_spec.rb | 10 ++- spec/styles/style_types_spec.rb | 6 +- spec/test_spec.rb | 72 ++++++++++--------- 21 files changed, 288 insertions(+), 252 deletions(-) create mode 100644 .rubocop.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fffbe9d..e310bcd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,12 +17,27 @@ permissions: contents: read jobs: + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.1" + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Run lint + run: bundle exec rubocop + test: runs-on: ubuntu-latest strategy: matrix: ruby-version: ['2.6', '2.7', '3.0', '3.1', '3.2'] + needs: + - lint steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index 650a897..4d78914 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ tmp # Mac finder artifacts .DS_Store + +.idea diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..2fce36d --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,27 @@ +Gemspec/RequiredRubyVersion: + Enabled: false + +Layout/LineLength: + Enabled: false +Metrics: + Enabled: false +Naming/ConstantName: + Enabled: false + +Style/FrozenStringLiteralComment: + Enabled: false +Style/Documentation: + Enabled: false +Style/AndOr: + Enabled: false +Style/StringConcatenation: + Enabled: false +Style/ClassAndModuleChildren: + Enabled: false +Style/OptionalBooleanParameter: + Enabled: false +Style/TernaryParentheses: + EnforcedStyle: require_parentheses_when_complex + +Naming/PredicateName: + Enabled: false diff --git a/Rakefile b/Rakefile index b8fe45b..6f6f4e6 100644 --- a/Rakefile +++ b/Rakefile @@ -1,7 +1,7 @@ -require "bundler/gem_tasks" +require 'bundler/gem_tasks' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new('spec') # If you want to make this the default task -task :default => :spec \ No newline at end of file +task default: :spec diff --git a/creek.gemspec b/creek.gemspec index ef6bce5..3fb2ca9 100644 --- a/creek.gemspec +++ b/creek.gemspec @@ -1,29 +1,29 @@ -# coding: utf-8 -lib = File.expand_path('../lib', __FILE__) +lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'creek/version' Gem::Specification.new do |spec| - spec.name = "creek" + spec.name = 'creek' spec.version = Creek::VERSION - spec.authors = ["pythonicrubyist"] - spec.email = ["pythonicrubyist@gmail.com"] - spec.description = %q{A Ruby gem that streams and parses large Excel(xlsx and xlsm) files fast and efficiently.} - spec.summary = %q{A Ruby gem for parsing large Excel(xlsx and xlsm) files.} - spec.homepage = "https://github.com/pythonicrubyist/creek" - spec.license = "MIT" + spec.authors = ['pythonicrubyist'] + spec.email = ['pythonicrubyist@gmail.com'] + spec.description = 'A Ruby gem that streams and parses large Excel(xlsx and xlsm) files fast and efficiently.' + spec.summary = 'A Ruby gem for parsing large Excel(xlsx and xlsm) files.' + spec.homepage = 'https://github.com/pythonicrubyist/creek' + spec.license = 'MIT' - spec.files = `git ls-files`.split($/) + spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) - spec.require_paths = ["lib"] + spec.require_paths = ['lib'] spec.required_ruby_version = '>= 2.0.0' - spec.add_development_dependency "bundler" - spec.add_development_dependency "rake" - spec.add_development_dependency 'rspec', '~> 3.6.0' + spec.add_development_dependency 'bundler' spec.add_development_dependency 'pry-byebug' + spec.add_development_dependency 'rake' + spec.add_development_dependency 'rspec', '~> 3.6.0' + spec.add_development_dependency 'rubocop' spec.add_dependency 'nokogiri', '>= 1.10.0' spec.add_dependency 'rubyzip', '>= 1.0.0' diff --git a/lib/creek/book.rb b/lib/creek/book.rb index 8fff8e6..157884e 100644 --- a/lib/creek/book.rb +++ b/lib/creek/book.rb @@ -14,11 +14,11 @@ class Creek::Book DATE_1900 = Date.new(1899, 12, 30).freeze DATE_1904 = Date.new(1904, 1, 1).freeze - def initialize path, options = {} + def initialize(path, options = {}) check_file_extension = options.fetch(:check_file_extension, true) if check_file_extension extension = File.extname(options[:original_filename] || path).downcase - raise 'Not a valid file format.' unless (['.xlsx', '.xlsm'].include? extension) + raise 'Not a valid file format.' unless ['.xlsx', '.xlsm'].include? extension end path = download_file(path) if options[:remote] @files = Zip::File.open(path) @@ -28,28 +28,26 @@ def initialize path, options = {} def sheets @sheets ||= begin - doc = @files.file.open "xl/workbook.xml" + doc = @files.file.open 'xl/workbook.xml' xml = Nokogiri::XML::Document.parse doc namespaces = xml.namespaces - cssPrefix = '' + css_prefix = '' namespaces.each do |namespace| - if namespace[1] == 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' && namespace[0] != 'xmlns' then - cssPrefix = namespace[0].split(':')[1]+'|' - end + css_prefix = namespace[0].split(':')[1] + '|' if namespace[1] == 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' && namespace[0] != 'xmlns' end - rels_doc = @files.file.open "xl/_rels/workbook.xml.rels" - rels = Nokogiri::XML::Document.parse(rels_doc).css("Relationship") - xml.css(cssPrefix+'sheet').map do |sheet| - sheetfile = rels.find { |el| sheet.attr("r:id") == el.attr("Id") }.attr("Target") + rels_doc = @files.file.open 'xl/_rels/workbook.xml.rels' + rels = Nokogiri::XML::Document.parse(rels_doc).css('Relationship') + xml.css(css_prefix + 'sheet').map do |sheet| + sheetfile = rels.find { |el| sheet.attr('r:id') == el.attr('Id') }.attr('Target') sheet = Sheet.new( self, - sheet.attr("name"), - sheet.attr("sheetid"), - sheet.attr("state"), - sheet.attr("visible"), - sheet.attr("r:id"), + sheet.attr('name'), + sheet.attr('sheetid'), + sheet.attr('state'), + sheet.attr('visible'), + sheet.attr('r:id'), sheetfile ) sheet.with_headers = with_headers @@ -68,23 +66,23 @@ def close def base_date @base_date ||= - begin - # Default to 1900 (minus one day due to excel quirk) but use 1904 if - # it's set in the Workbook's workbookPr - # http://msdn.microsoft.com/en-us/library/ff530155(v=office.12).aspx - result = DATE_1900 # default + begin + # Default to 1900 (minus one day due to excel quirk) but use 1904 if + # it's set in the Workbook's workbookPr + # http://msdn.microsoft.com/en-us/library/ff530155(v=office.12).aspx + result = DATE_1900 # default - doc = @files.file.open "xl/workbook.xml" - xml = Nokogiri::XML::Document.parse doc - xml.css('workbookPr[date1904]').each do |workbookPr| - if workbookPr['date1904'] =~ /true|1/i - result = DATE_1904 - break + doc = @files.file.open 'xl/workbook.xml' + xml = Nokogiri::XML::Document.parse doc + xml.css('workbookPr[date1904]').each do |workbook_pr| + if workbook_pr['date1904'] =~ /true|1/i + result = DATE_1904 + break + end end - end - result - end + result + end end private diff --git a/lib/creek/drawing.rb b/lib/creek/drawing.rb index 7847536..4df24ce 100644 --- a/lib/creek/drawing.rb +++ b/lib/creek/drawing.rb @@ -15,10 +15,10 @@ def initialize(book, drawing_filepath) @drawings_rels = [] @images_pathnames = Hash.new { |hash, key| hash[key] = [] } - if file_exist?(@drawing_filepath) - load_drawings_and_rels - load_images_pathnames_by_cells if has_images? - end + return unless file_exist?(@drawing_filepath) + + load_drawings_and_rels + load_images_pathnames_by_cells if has_images? end ## @@ -36,13 +36,11 @@ def images_at(cell_name) return if pathnames_at_coordinate.empty? pathnames_at_coordinate.map do |image_pathname| - if image_pathname.exist? - image_pathname - else + unless image_pathname.exist? excel_image_path = "xl/media#{image_pathname.to_path.split(tmpdir).last}" IO.copy_stream(@book.files.file.open(excel_image_path), image_pathname.to_path) - image_pathname - end + end + image_pathname end end @@ -52,8 +50,8 @@ def images_at(cell_name) # Transforms cell name to [row, col], e.g. A1 => [0, 0], B3 => [1, 2] # Rows and cols start with 0. def calc_coordinate(cell_name) - col = COLUMNS.index(cell_name.slice /[A-Z]+/) - row = (cell_name.slice /\d+/).to_i - 1 # rows in drawings start with 0 + col = COLUMNS.index(cell_name.slice(/[A-Z]+/)) + row = cell_name.slice(/\d+/).to_i - 1 # rows in drawings start with 0 [row, col] end @@ -68,7 +66,7 @@ def tmpdir # Drawing xml contains relationships ID's and coordinates (row, col). # Drawing relationships xml contains images' locations. def load_drawings_and_rels - @drawings = parse_xml(@drawing_filepath).css('xdr|twoCellAnchor', 'xdr|oneCellAnchor' ) + @drawings = parse_xml(@drawing_filepath).css('xdr|twoCellAnchor', 'xdr|oneCellAnchor') drawing_rels_filepath = expand_to_rels_path(@drawing_filepath) @drawings_rels = parse_xml(drawing_rels_filepath).css('Relationships') end @@ -78,11 +76,11 @@ def load_drawings_and_rels # As multiple images can be located in a single cell, hash values are array of Pathname objects. # One image can be spread across multiple cells (defined with from-row/to-row/from-col/to-col attributes) - same Pathname object is associated to each row-col combination for the range. def load_images_pathnames_by_cells - image_selector = 'xdr:pic/xdr:blipFill/a:blip'.freeze - row_from_selector = 'xdr:from/xdr:row'.freeze - row_to_selector = 'xdr:to/xdr:row'.freeze - col_from_selector = 'xdr:from/xdr:col'.freeze - col_to_selector = 'xdr:to/xdr:col'.freeze + image_selector = 'xdr:pic/xdr:blipFill/a:blip' + row_from_selector = 'xdr:from/xdr:row' + row_to_selector = 'xdr:to/xdr:row' + col_from_selector = 'xdr:from/xdr:col' + col_to_selector = 'xdr:to/xdr:col' @drawings.xpath('//xdr:twoCellAnchor', '//xdr:oneCellAnchor').each do |drawing| # embed = drawing.xpath(image_selector).first.attributes['embed'] @@ -91,13 +89,13 @@ def load_images_pathnames_by_cells next if embed.nil? rid = embed.value - path = Pathname.new("#{tmpdir}/#{extract_drawing_path(rid).slice(/[^\/]*$/)}") + path = Pathname.new("#{tmpdir}/#{extract_drawing_path(rid).slice(%r{[^/]*$})}") row_from = drawing.xpath(row_from_selector).text.to_i col_from = drawing.xpath(col_from_selector).text.to_i if drawing.name == 'oneCellAnchor' - @images_pathnames[[row_from , col_from ]].push(path) + @images_pathnames[[row_from, col_from]].push(path) else row_to = drawing.xpath(row_to_selector).text.to_i col_to = drawing.xpath(col_to_selector).text.to_i diff --git a/lib/creek/shared_strings.rb b/lib/creek/shared_strings.rb index 663aaf1..2b32e6b 100644 --- a/lib/creek/shared_strings.rb +++ b/lib/creek/shared_strings.rb @@ -4,25 +4,23 @@ require 'nokogiri' module Creek - class Creek::SharedStrings - SPREADSHEETML_URI = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main' attr_reader :book, :dictionary - def initialize book + def initialize(book) @book = book parse_shared_shared_strings end def parse_shared_shared_strings - path = "xl/sharedStrings.xml" - if @book.files.file.exist?(path) - doc = @book.files.file.open path - xml = Nokogiri::XML::Document.parse doc - parse_shared_string_from_document(xml) - end + path = 'xl/sharedStrings.xml' + return unless @book.files.file.exist?(path) + + doc = @book.files.file.open path + xml = Nokogiri::XML::Document.parse doc + parse_shared_string_from_document(xml) end def parse_shared_string_from_document(xml) @@ -30,8 +28,8 @@ def parse_shared_string_from_document(xml) end def self.parse_shared_string_from_document(xml) - dictionary = Hash.new - namespace = xml.namespaces.detect{|_key, uri| uri == SPREADSHEETML_URI } + dictionary = {} + namespace = xml.namespaces.detect { |_key, uri| uri == SPREADSHEETML_URI } prefix = if namespace && namespace[0].start_with?('xmlns:') namespace[0].delete_prefix('xmlns:') + '|' else @@ -42,15 +40,14 @@ def self.parse_shared_string_from_document(xml) xml.css(node_selector).each_with_index do |si, idx| text_nodes = si.css(text_selector) - if text_nodes.count == 1 # plain text node - dictionary[idx] = Creek::Styles::Converter.unescape_string(text_nodes.first.content) - else # rich text nodes with text fragments - dictionary[idx] = text_nodes.map { |n| Creek::Styles::Converter.unescape_string(n.content) }.join('') - end + dictionary[idx] = if text_nodes.count == 1 # plain text node + Creek::Styles::Converter.unescape_string(text_nodes.first.content) + else # rich text nodes with text fragments + text_nodes.map { |n| Creek::Styles::Converter.unescape_string(n.content) }.join('') + end end dictionary end - end end diff --git a/lib/creek/sheet.rb b/lib/creek/sheet.rb index 1df94b8..69d8497 100644 --- a/lib/creek/sheet.rb +++ b/lib/creek/sheet.rb @@ -85,65 +85,67 @@ def simple_rows_with_meta_data ## # Returns a hash per row that includes the cell ids and values. # Empty cells will be also included in the hash with a nil value. - def rows_generator include_meta_data=false, use_simple_rows_format=false - path = if @sheetfile.start_with? "/xl/" or @sheetfile.start_with? "xl/" then @sheetfile else "xl/#{@sheetfile}" end - if @book.files.file.exist?(path) - # SAX parsing, Each element in the stream comes through as two events: - # one to open the element and one to close it. - opener = Nokogiri::XML::Reader::TYPE_ELEMENT - closer = Nokogiri::XML::Reader::TYPE_END_ELEMENT - Enumerator.new do |y| - @headers = nil - row, cells, cell = nil, {}, nil - cell_type = nil - cell_style_idx = nil - @book.files.file.open(path) do |xml| - prefix = '' - name_row = "row" - name_c = "c" - name_v = "v" - name_t = "t" - Nokogiri::XML::Reader.from_io(xml).each do |node| - if prefix.empty? && node.namespaces.any? - namespace = node.namespaces.detect{|_key, uri| uri == SPREADSHEETML_URI } - prefix = if namespace && namespace[0].start_with?('xmlns:') - namespace[0].delete_prefix('xmlns:') + ':' - else - '' - end - name_row = "#{prefix}row" - name_c = "#{prefix}c" - name_v = "#{prefix}v" - name_t = "#{prefix}t" - end - if node.name == name_row && node.node_type == opener - row = node.attributes - row['cells'] = {} - cells = {} - y << (include_meta_data ? row : cells) if node.self_closing? - elsif node.name == name_row && node.node_type == closer - processed_cells = fill_in_empty_cells(cells, row['r'], cell, use_simple_rows_format) - @headers = processed_cells if with_headers && row['r'] == HEADERS_ROW_NUMBER - - if @images_present - processed_cells.each do |cell_name, cell_value| - next unless cell_value.nil? - - processed_cells[cell_name] = images_at(cell_name) - end + def rows_generator(include_meta_data = false, use_simple_rows_format = false) + path = (@sheetfile.start_with? '/xl/' or @sheetfile.start_with? 'xl/') ? @sheetfile : "xl/#{@sheetfile}" + return unless @book.files.file.exist?(path) + + # SAX parsing, Each element in the stream comes through as two events: + # one to open the element and one to close it. + opener = Nokogiri::XML::Reader::TYPE_ELEMENT + closer = Nokogiri::XML::Reader::TYPE_END_ELEMENT + Enumerator.new do |y| + @headers = nil + row = nil + cells = {} + cell = nil + cell_type = nil + cell_style_idx = nil + @book.files.file.open(path) do |xml| + prefix = '' + name_row = 'row' + name_c = 'c' + name_v = 'v' + name_t = 't' + Nokogiri::XML::Reader.from_io(xml).each do |node| + if prefix.empty? && node.namespaces.any? + namespace = node.namespaces.detect { |_key, uri| uri == SPREADSHEETML_URI } + prefix = if namespace && namespace[0].start_with?('xmlns:') + namespace[0].delete_prefix('xmlns:') + ':' + else + '' + end + name_row = "#{prefix}row" + name_c = "#{prefix}c" + name_v = "#{prefix}v" + name_t = "#{prefix}t" + end + if node.name == name_row && node.node_type == opener + row = node.attributes + row['cells'] = {} + cells = {} + y << (include_meta_data ? row : cells) if node.self_closing? + elsif node.name == name_row && node.node_type == closer + processed_cells = fill_in_empty_cells(cells, row['r'], cell, use_simple_rows_format) + @headers = processed_cells if with_headers && row['r'] == HEADERS_ROW_NUMBER + + if @images_present + processed_cells.each do |cell_name, cell_value| + next unless cell_value.nil? + + processed_cells[cell_name] = images_at(cell_name) end + end - row['cells'] = processed_cells - y << (include_meta_data ? row : processed_cells) - elsif node.name == name_c && node.node_type == opener - cell_type = node.attributes['t'] - cell_style_idx = node.attributes['s'] - cell = node.attributes['r'] - elsif (node.name == name_v || node.name == name_t) && node.node_type == opener - unless cell.nil? - node.read - cells[cell] = convert(node.value, cell_type, cell_style_idx) - end + row['cells'] = processed_cells + y << (include_meta_data ? row : processed_cells) + elsif node.name == name_c && node.node_type == opener + cell_type = node.attributes['t'] + cell_style_idx = node.attributes['s'] + cell = node.attributes['r'] + elsif (node.name == name_v || node.name == name_t) && node.node_type == opener + unless cell.nil? + node.read + cells[cell] = convert(node.value, cell_type, cell_style_idx) end end end @@ -199,7 +201,7 @@ def extract_drawing_filepath def cell_id(column, use_simple_rows_format, row_number) return "#{column}#{row_number}" unless use_simple_rows_format - with_headers && headers ? headers[column] : column + (with_headers && headers) ? headers[column] : column end end end diff --git a/lib/creek/styles.rb b/lib/creek/styles.rb index 016f158..d598c17 100644 --- a/lib/creek/styles.rb +++ b/lib/creek/styles.rb @@ -3,27 +3,24 @@ module Creek class Styles attr_accessor :book + def initialize(book) @book = book end def path - "xl/styles.xml" + 'xl/styles.xml' end def styles_xml - @styles_xml ||= begin - if @book.files.file.exist?(path) - doc = @book.files.file.open path - Nokogiri::XML::Document.parse doc - end - end + @styles_xml ||= if @book.files.file.exist?(path) + doc = @book.files.file.open path + Nokogiri::XML::Document.parse doc + end end def style_types - @style_types ||= begin - Creek::Styles::StyleTypes.new(styles_xml).call - end + @style_types ||= Creek::Styles::StyleTypes.new(styles_xml).call end end end diff --git a/lib/creek/styles/constants.rb b/lib/creek/styles/constants.rb index 4cde485..163d9e0 100644 --- a/lib/creek/styles/constants.rb +++ b/lib/creek/styles/constants.rb @@ -3,16 +3,16 @@ class Styles module Constants # Map of non-custom numFmtId to casting symbol NumFmtMap = { - 0 => :string, # General - 1 => :fixnum, # 0 - 2 => :float, # 0.00 - 3 => :fixnum, # #,##0 - 4 => :float, # #,##0.00 - 5 => :unsupported, # $#,##0_);($#,##0) - 6 => :unsupported, # $#,##0_);[Red]($#,##0) - 7 => :unsupported, # $#,##0.00_);($#,##0.00) - 8 => :unsupported, # $#,##0.00_);[Red]($#,##0.00) - 9 => :percentage, # 0% + 0 => :string, # General + 1 => :fixnum, # 0 + 2 => :float, # 0.00 + 3 => :fixnum, # #,##0 + 4 => :float, # #,##0.00 + 5 => :unsupported, # $#,##0_);($#,##0) + 6 => :unsupported, # $#,##0_);[Red]($#,##0) + 7 => :unsupported, # $#,##0.00_);($#,##0.00) + 8 => :unsupported, # $#,##0.00_);[Red]($#,##0.00) + 9 => :percentage, # 0% 10 => :percentage, # 0.00% 11 => :bignum, # 0.00E+00 12 => :unsupported, # # ?/? @@ -35,7 +35,7 @@ module Constants 47 => :time, # mmss.0 48 => :bignum, # ##0.0E+0 49 => :unsupported # @ - } + }.freeze end end end diff --git a/lib/creek/styles/converter.rb b/lib/creek/styles/converter.rb index 62bd6d6..08d7047 100644 --- a/lib/creek/styles/converter.rb +++ b/lib/creek/styles/converter.rb @@ -8,7 +8,7 @@ class Converter include Creek::Styles::Constants # Excel non-printable character escape sequence - HEX_ESCAPE_REGEXP = /_x[0-9A-Fa-f]{4}_/ + HEX_ESCAPE_REGEXP = /_x[0-9A-Fa-f]{4}_/.freeze ## # The heart of typecasting. The ruby type is determined either explicitly @@ -29,14 +29,12 @@ class Converter # - shared_strings: needed for 's' (shared string) type # - base_date: from what date to begin, see method #base_date - DATE_TYPES = [:date, :time, :date_time].to_set + DATE_TYPES = %i[date time date_time].to_set def self.call(value, type, style, options = {}) return nil if value.nil? || value.empty? # Sometimes the type is dictated by the style alone - if type.nil? || (type == 'n' && DATE_TYPES.include?(style)) - type = style - end + type = style if type.nil? || (type == 'n' && DATE_TYPES.include?(style)) case type @@ -80,21 +78,19 @@ def self.call(value, type, style, options = {}) convert_unknown(value) end end - + def self.convert_unknown(value) - begin - if value.nil? or value.empty? - return value - elsif value.to_i.to_s == value.to_s - return value.to_i - elsif value.to_f.to_s == value.to_s - return value.to_f - else - return value - end - rescue - return value + if value.nil? or value.empty? + value + elsif value.to_i.to_s == value.to_s + value.to_i + elsif value.to_f.to_s == value.to_s + value.to_f + else + value end + rescue StandardError + value end def self.convert_date(value, options) @@ -124,17 +120,15 @@ def self.unescape_string(value) value.gsub(HEX_ESCAPE_REGEXP) { |match| match[2, 4].to_i(16).chr(Encoding::UTF_8) } end - private - - def self.base_date(options) - options.fetch(:base_date, Date.new(1899, 12, 30)) - end + def self.base_date(options) + options.fetch(:base_date, Date.new(1899, 12, 30)) + end - def self.round_datetime(datetime_string) - /(?\d+)-(?\d+)-(?

\d+) (?\d+):(?\d+):(?\d+.\d+)/ =~ datetime_string + def self.round_datetime(datetime_string) + /(?\d+)-(?\d+)-(?
\d+) (?\d+):(?\d+):(?\d+.\d+)/ =~ datetime_string - ::Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0) - end + ::Time.new(yyyy.to_i, mm.to_i, dd.to_i, hh.to_i, mi.to_i, ss.to_r).round(0) + end end end end diff --git a/lib/creek/styles/style_types.rb b/lib/creek/styles/style_types.rb index df3b4d1..0f34dc2 100644 --- a/lib/creek/styles/style_types.rb +++ b/lib/creek/styles/style_types.rb @@ -7,6 +7,7 @@ class Styles class StyleTypes include Creek::Styles::Constants attr_accessor :styles_xml_doc + def initialize(styles_xml_doc) @styles_xml_doc = styles_xml_doc end @@ -26,17 +27,18 @@ def initialize(styles_xml_doc) # custom). Hence this style types array, rather than a map of numFmtId to # type. def call - @style_types ||= begin - styles_xml_doc.css('styleSheet cellXfs xf').map do |xstyle| - a = num_fmt_id(xstyle) - style_type_by_num_fmt_id( a ) - end + # rubocop:disable Naming/MemoizedInstanceVariableName + @style_types ||= styles_xml_doc.css('styleSheet cellXfs xf').map do |xstyle| + a = num_fmt_id(xstyle) + style_type_by_num_fmt_id(a) end + # rubocop:enable Naming/MemoizedInstanceVariableName end - #returns the numFmtId value if it's available + # returns the numFmtId value if it's available def num_fmt_id(xstyle) return nil unless xstyle.attributes['numFmtId'] + xstyle.attributes['numFmtId'].value end @@ -50,6 +52,7 @@ def num_fmt_id(xstyle) # like a bad idea, but we try to be flexible and just go with it. def style_type_by_num_fmt_id(id) return nil unless id + id = id.to_i NumFmtMap[id] || custom_style_types[id] end @@ -57,13 +60,10 @@ def style_type_by_num_fmt_id(id) # Map of (numFmtId >= 164) (custom styles) to our best guess at the type # ex. {164 => :date_time} def custom_style_types - @custom_style_types ||= begin - styles_xml_doc.css('styleSheet numFmts numFmt').inject({}) do |acc, xstyle| - index = xstyle.attributes['numFmtId'].value.to_i - value = xstyle.attributes['formatCode'].value - acc[index] = determine_custom_style_type(value) - acc - end + @custom_style_types ||= styles_xml_doc.css('styleSheet numFmts numFmt').each_with_object({}) do |xstyle, acc| + index = xstyle.attributes['numFmtId'].value.to_i + value = xstyle.attributes['formatCode'].value + acc[index] = determine_custom_style_type(value) end end @@ -80,7 +80,7 @@ def determine_custom_style_type(string) # Looks for one of ymdhis outside of meta-stuff like [Red] return :date_time if string =~ /(^|\])[^\[]*[ymdhis]/i - return :unsupported + :unsupported end end end diff --git a/lib/creek/utils.rb b/lib/creek/utils.rb index af4ac90..5b87163 100644 --- a/lib/creek/utils.rb +++ b/lib/creek/utils.rb @@ -3,7 +3,7 @@ module Creek module Utils def expand_to_rels_path(filepath) - filepath.sub(/(\/[^\/]+$)/, '/_rels\1.rels') + filepath.sub(%r{(/[^/]+$)}, '/_rels\1.rels') end def file_exist?(path) diff --git a/lib/creek/version.rb b/lib/creek/version.rb index 7cb8f1e..7c70ce4 100644 --- a/lib/creek/version.rb +++ b/lib/creek/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Creek - VERSION = "2.6.3" + VERSION = '2.6.3' end diff --git a/spec/drawing_spec.rb b/spec/drawing_spec.rb index 705941f..2832a5d 100644 --- a/spec/drawing_spec.rb +++ b/spec/drawing_spec.rb @@ -3,7 +3,9 @@ describe 'drawing' do let(:book) { Creek::Book.new('spec/fixtures/sample-with-images.xlsx') } let(:book_no_images) { Creek::Book.new('spec/fixtures/sample.xlsx') } - let(:book_with_one_cell_anchored_images) { Creek::Book.new('spec/fixtures/sample-with-one-cell-anchored-images.xlsx') } + let(:book_with_one_cell_anchored_images) do + Creek::Book.new('spec/fixtures/sample-with-one-cell-anchored-images.xlsx') + end let(:drawingfile) { 'xl/drawings/drawing1.xml' } let(:drawing) { Creek::Drawing.new(book, drawingfile) } let(:drawing_without_images) { Creek::Drawing.new(book_no_images, drawingfile) } diff --git a/spec/shared_string_spec.rb b/spec/shared_string_spec.rb index 11b0130..6fb4b88 100644 --- a/spec/shared_string_spec.rb +++ b/spec/shared_string_spec.rb @@ -1,7 +1,6 @@ require './spec/spec_helper' describe 'shared strings' do - it 'parses rich text strings correctly' do shared_strings_xml_file = File.open('spec/fixtures/sst.xml') doc = Nokogiri::XML(shared_strings_xml_file) @@ -32,6 +31,5 @@ expect(dictionary[5]).to eq("Cell with\rescaped\rcharacters") expect(dictionary[6]).to eq('吉田兼好') end - end end diff --git a/spec/sheet_spec.rb b/spec/sheet_spec.rb index eb7b521..6ecee45 100644 --- a/spec/sheet_spec.rb +++ b/spec/sheet_spec.rb @@ -66,8 +66,12 @@ def load_cell(rows, cell_name) end context 'when one cell anchored images in cell' do - let(:book_with_one_cell_anchored_images) { Creek::Book.new('spec/fixtures/sample-with-one-cell-anchored-images.xlsx') } - let(:sheet_with_one_cell_anchored_images) { Creek::Sheet.new(book_with_one_cell_anchored_images, 'Sheet 1', 1, '', '', '1', sheetfile) } + let(:book_with_one_cell_anchored_images) do + Creek::Book.new('spec/fixtures/sample-with-one-cell-anchored-images.xlsx') + end + let(:sheet_with_one_cell_anchored_images) do + Creek::Sheet.new(book_with_one_cell_anchored_images, 'Sheet 1', 1, '', '', '1', sheetfile) + end let(:rows) { sheet_with_one_cell_anchored_images.with_images.rows.map { |r| r } } it 'returns image for anchored cell' do diff --git a/spec/styles/converter_spec.rb b/spec/styles/converter_spec.rb index a86e75c..ea44e8e 100644 --- a/spec/styles/converter_spec.rb +++ b/spec/styles/converter_spec.rb @@ -1,22 +1,20 @@ require './spec/spec_helper' describe Creek::Styles::Converter do - describe :call do - def convert(value, type, style) Creek::Styles::Converter.call(value, type, style) end describe :date do - it "works" do - expect(convert('41275', 'n', :date)).to eq(Date.new(2013,01,01)) + it 'works' do + expect(convert('41275', 'n', :date)).to eq(Date.new(2013, 0o1, 0o1)) end end describe :date_time do - it "works" do - expect(convert('41275', 'n', :date_time)).to eq(Time.new(2013,01,01)) + it 'works' do + expect(convert('41275', 'n', :date_time)).to eq(Time.new(2013, 0o1, 0o1)) end end end diff --git a/spec/styles/style_types_spec.rb b/spec/styles/style_types_spec.rb index b5c72bc..c737908 100644 --- a/spec/styles/style_types_spec.rb +++ b/spec/styles/style_types_spec.rb @@ -1,15 +1,15 @@ require './spec/spec_helper' describe Creek::Styles::StyleTypes do - describe :call do - it "return array of styletypes with mapping to ruby types" do + it 'return array of styletypes with mapping to ruby types' do xml_file = File.open('spec/fixtures/styles/first.xml') doc = Nokogiri::XML(xml_file) res = Creek::Styles::StyleTypes.new(doc).call expect(res.size).to eq(8) expect(res[3]).to eq(:date_time) - expect(res).to eq([:unsupported, :unsupported, :unsupported, :date_time, :unsupported, :unsupported, :unsupported, :unsupported]) + expect(res).to eq(%i[unsupported unsupported unsupported date_time unsupported unsupported + unsupported unsupported]) end end end diff --git a/spec/test_spec.rb b/spec/test_spec.rb index dabb932..6ff1fff 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -19,9 +19,9 @@ it 'Check file extension of original_filename if passed.' do path = 'spec/fixtures/temp_string_io_file_path_with_no_extension' - expect { Creek::Book.new path, :original_filename => 'invalid.xls' } + expect { Creek::Book.new path, original_filename: 'invalid.xls' } .to raise_error 'Not a valid file format.' - expect { Creek::Book.new path, :original_filename => 'valid.xlsx' } + expect { Creek::Book.new path, original_filename: 'valid.xlsx' } .not_to raise_error end end @@ -31,9 +31,10 @@ @creek = Creek::Book.new 'spec/fixtures/sample_dates.xlsx' @expected_datetime_rows = [ - {'A3' => 'Date', 'B3' => Date.parse('2018-01-01')}, - {'A4' => 'Datetime 00:00:00', 'B4' => Time.parse('2018-01-01 00:00:00')}, - {'A5' => 'Datetime', 'B5' => Time.parse('2018-01-01 23:59:59')}] + { 'A3' => 'Date', 'B3' => Date.parse('2018-01-01') }, + { 'A4' => 'Datetime 00:00:00', 'B4' => Time.parse('2018-01-01 00:00:00') }, + { 'A5' => 'Datetime', 'B5' => Time.parse('2018-01-01 23:59:59') } + ] end after(:all) do @@ -41,7 +42,7 @@ end it 'parses dates successfully' do - rows = Array.new + rows = [] row_count = 0 @creek.sheets[0].rows.each do |row| rows << row @@ -49,7 +50,7 @@ end (2..5).each do |number| - expect(rows[number]).to eq(@expected_datetime_rows[number-2]) + expect(rows[number]).to eq(@expected_datetime_rows[number - 2]) end end end @@ -57,7 +58,7 @@ describe 'Creek parsing a file with large numbrts.' do before(:all) do @creek = Creek::Book.new 'spec/fixtures/large_numbers.xlsx' - @expected_simple_rows = [{"A"=>"7.83294732E8", "B"=>"783294732", "C"=>783294732.0}] + @expected_simple_rows = [{ 'A' => '7.83294732E8', 'B' => '783294732', 'C' => 783_294_732.0 }] end after(:all) do @@ -65,7 +66,7 @@ end it 'Parse simple rows successfully.' do - rows = Array.new + rows = [] row_count = 0 @creek.sheets[0].simple_rows.each do |row| rows << row @@ -78,24 +79,28 @@ describe 'Creek parsing a sample XLSX file' do before(:all) do @creek = Creek::Book.new 'spec/fixtures/sample.xlsx' - @expected_rows = [{'A1'=>'Content 1', 'B1'=>nil, 'C1'=>'Content 2', 'D1'=>nil, 'E1'=>'Content 3'}, - {'A2'=>nil, 'B2'=>'Content 4', 'C2'=>nil, 'D2'=>'Content 5', 'E2'=>nil, 'F2'=>'Content 6'}, - {}, - {'A4'=>'Content 7', 'B4'=>'Content 8', 'C4'=>'Content 9', 'D4'=>'Content 10', 'E4'=>'Content 11', 'F4'=>'Content 12'}, - {'A5'=>nil, 'B5'=>nil, 'C5'=>nil, 'D5'=>nil, 'E5'=>nil, 'F5'=>nil, 'G5'=>nil, 'H5'=>nil, 'I5'=>nil, 'J5'=>nil, 'K5'=>nil, 'L5'=>nil, 'M5'=>nil, 'N5'=>nil, 'O5'=>nil, 'P5'=>nil, 'Q5'=>nil, 'R5'=>nil, 'S5'=>nil, 'T5'=>nil, 'U5'=>nil, 'V5'=>nil, 'W5'=>nil, 'X5'=>nil, 'Y5'=>nil, 'Z5'=>'Z Content', 'AA5'=>nil, 'AB5'=>nil, 'AC5'=>nil, 'AD5'=>nil, 'AE5'=>nil, 'AF5'=>nil, 'AG5'=>nil, 'AH5'=>nil, 'AI5'=>nil, 'AJ5'=>nil, 'AK5'=>nil, 'AL5'=>nil, 'AM5'=>nil, 'AN5'=>nil, 'AO5'=>nil, 'AP5'=>nil, 'AQ5'=>nil, 'AR5'=>nil, 'AS5'=>nil, 'AT5'=>nil, 'AU5'=>nil, 'AV5'=>nil, 'AW5'=>nil, 'AX5'=>nil, 'AY5'=>nil, 'AZ5'=>'Content 13'}, - {'A6'=>'1', 'B6'=>'2', 'C6'=>'3'}, {'A7'=>'Content 15', 'B7'=>'Content 16', 'C7'=>'Content 18', 'D7'=>'Content 19'}, - {'A8'=>nil, 'B8'=>'Content 20', 'C8'=>nil, 'D8'=>nil, 'E8'=>nil, 'F8'=>'Content 21'}, - {'A10' => 0.15, 'B10' => 0.15}] - - @expected_simple_rows = [{"A"=>"Content 1", "B"=>nil, "C"=>"Content 2", "D"=>nil, "E"=>"Content 3"}, - {"A"=>nil, "B"=>"Content 4", "C"=>nil, "D"=>"Content 5", "E"=>nil, "F"=>"Content 6"}, - {}, - {"A"=>"Content 7", "B"=>"Content 8", "C"=>"Content 9", "D"=>"Content 10", "E"=>"Content 11", "F"=>"Content 12"}, - {"A"=>nil, "B"=>nil, "C"=>nil, "D"=>nil, "E"=>nil, "F"=>nil, "G"=>nil, "H"=>nil, "I"=>nil, "J"=>nil, "K"=>nil, "L"=>nil, "M"=>nil, "N"=>nil, "O"=>nil, "P"=>nil, "Q"=>nil, "R"=>nil, "S"=>nil, "T"=>nil, "U"=>nil, "V"=>nil, "W"=>nil, "X"=>nil, "Y"=>nil, "Z"=>"Z Content", "AA"=>nil, "AB"=>nil, "AC"=>nil, "AD"=>nil, "AE"=>nil, "AF"=>nil, "AG"=>nil, "AH"=>nil, "AI"=>nil, "AJ"=>nil, "AK"=>nil, "AL"=>nil, "AM"=>nil, "AN"=>nil, "AO"=>nil, "AP"=>nil, "AQ"=>nil, "AR"=>nil, "AS"=>nil, "AT"=>nil, "AU"=>nil, "AV"=>nil, "AW"=>nil, "AX"=>nil, "AY"=>nil, "AZ"=>"Content 13"}, - {"A"=>"1", "B"=>"2", "C"=>"3"}, - {"A"=>"Content 15", "B"=>"Content 16", "C"=>"Content 18", "D"=>"Content 19"}, - {"A"=>nil, "B"=>"Content 20", "C"=>nil, "D"=>nil, "E"=>nil, "F"=>"Content 21"}, - {"A"=>0.15, "B"=>0.15}] + @expected_rows = [{ 'A1' => 'Content 1', 'B1' => nil, 'C1' => 'Content 2', 'D1' => nil, 'E1' => 'Content 3' }, + { 'A2' => nil, 'B2' => 'Content 4', 'C2' => nil, 'D2' => 'Content 5', 'E2' => nil, 'F2' => 'Content 6' }, + {}, + { 'A4' => 'Content 7', 'B4' => 'Content 8', 'C4' => 'Content 9', 'D4' => 'Content 10', 'E4' => 'Content 11', 'F4' => 'Content 12' }, + { 'A5' => nil, 'B5' => nil, 'C5' => nil, 'D5' => nil, 'E5' => nil, 'F5' => nil, 'G5' => nil, 'H5' => nil, 'I5' => nil, 'J5' => nil, 'K5' => nil, 'L5' => nil, 'M5' => nil, 'N5' => nil, 'O5' => nil, 'P5' => nil, 'Q5' => nil, 'R5' => nil, 'S5' => nil, 'T5' => nil, 'U5' => nil, 'V5' => nil, 'W5' => nil, 'X5' => nil, 'Y5' => nil, 'Z5' => 'Z Content', 'AA5' => nil, 'AB5' => nil, 'AC5' => nil, 'AD5' => nil, 'AE5' => nil, 'AF5' => nil, 'AG5' => nil, 'AH5' => nil, 'AI5' => nil, 'AJ5' => nil, 'AK5' => nil, 'AL5' => nil, 'AM5' => nil, 'AN5' => nil, 'AO5' => nil, 'AP5' => nil, 'AQ5' => nil, 'AR5' => nil, 'AS5' => nil, 'AT5' => nil, 'AU5' => nil, 'AV5' => nil, 'AW5' => nil, 'AX5' => nil, 'AY5' => nil, 'AZ5' => 'Content 13' }, + { 'A6' => '1', 'B6' => '2', 'C6' => '3' }, { 'A7' => 'Content 15', 'B7' => 'Content 16', 'C7' => 'Content 18', 'D7' => 'Content 19' }, + { 'A8' => nil, 'B8' => 'Content 20', 'C8' => nil, 'D8' => nil, 'E8' => nil, 'F8' => 'Content 21' }, + { 'A10' => 0.15, 'B10' => 0.15 }] + + @expected_simple_rows = [{ 'A' => 'Content 1', 'B' => nil, 'C' => 'Content 2', 'D' => nil, 'E' => 'Content 3' }, + { 'A' => nil, 'B' => 'Content 4', 'C' => nil, 'D' => 'Content 5', 'E' => nil, + 'F' => 'Content 6' }, + {}, + { 'A' => 'Content 7', 'B' => 'Content 8', 'C' => 'Content 9', 'D' => 'Content 10', 'E' => 'Content 11', + 'F' => 'Content 12' }, + { 'A' => nil, 'B' => nil, 'C' => nil, 'D' => nil, 'E' => nil, 'F' => nil, 'G' => nil, 'H' => nil, 'I' => nil, + 'J' => nil, 'K' => nil, 'L' => nil, 'M' => nil, 'N' => nil, 'O' => nil, 'P' => nil, 'Q' => nil, 'R' => nil, 'S' => nil, 'T' => nil, 'U' => nil, 'V' => nil, 'W' => nil, 'X' => nil, 'Y' => nil, 'Z' => 'Z Content', 'AA' => nil, 'AB' => nil, 'AC' => nil, 'AD' => nil, 'AE' => nil, 'AF' => nil, 'AG' => nil, 'AH' => nil, 'AI' => nil, 'AJ' => nil, 'AK' => nil, 'AL' => nil, 'AM' => nil, 'AN' => nil, 'AO' => nil, 'AP' => nil, 'AQ' => nil, 'AR' => nil, 'AS' => nil, 'AT' => nil, 'AU' => nil, 'AV' => nil, 'AW' => nil, 'AX' => nil, 'AY' => nil, 'AZ' => 'Content 13' }, + { 'A' => '1', 'B' => '2', 'C' => '3' }, + { 'A' => 'Content 15', 'B' => 'Content 16', 'C' => 'Content 18', 'D' => 'Content 19' }, + { 'A' => nil, 'B' => 'Content 20', 'C' => nil, 'D' => nil, 'E' => nil, + 'F' => 'Content 21' }, + { 'A' => 0.15, 'B' => 0.15 }] end after(:all) do @@ -129,40 +134,39 @@ end it 'Parse simple rows successfully.' do - rows = Array.new + rows = [] row_count = 0 @creek.sheets[0].simple_rows.each do |row| rows << row row_count += 1 end - (0..8).each do |number| + 9.times do |number| expect(rows[number]).to eq(@expected_simple_rows[number]) end expect(row_count).to eq(9) end - it 'Parse rows with empty cells successfully.' do - rows = Array.new + rows = [] row_count = 0 @creek.sheets[0].rows.each do |row| rows << row row_count += 1 end - (0..8).each do |number| + 9.times do |number| expect(rows[number]).to eq(@expected_rows[number]) end expect(row_count).to eq(9) end it 'Parse rows with empty cells and meta data successfully.' do - rows = Array.new + rows = [] row_count = 0 @creek.sheets[0].rows_with_meta_data.each do |row| rows << row row_count += 1 end - expect(rows.map{|r| r['cells']}).to eq(@expected_rows) + expect(rows.map { |r| r['cells'] }).to eq(@expected_rows) end end From 39bffae1cb8d2cf803614a999c488d0a110edb45 Mon Sep 17 00:00:00 2001 From: Ramtin Vaziri Date: Tue, 23 Dec 2025 22:25:13 -0500 Subject: [PATCH 61/61] Update LICENSE.txt --- LICENSE.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index 2e951ec..6406998 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2017 Ramtin Vaziri +Copyright (c) 2017 Ramtin Vaziri https://www.ramtin-vaziri.com MIT License @@ -20,3 +20,5 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +https://ramtin-vaziri.com