Commit a7247cad authored by Christopher Huhn's avatar Christopher Huhn
Browse files

New upstream version 3.4.25

parent a1c4dd27
# Ruby CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/ruby:2.6.6-buster
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/postgres:9.4
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run:
name: install dependencies
command: |
gem install bundler
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
# run tests!
- run:
name: run tests
command: |
mkdir /tmp/test-results
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
circleci tests split --split-by=timings)"
bundle exec rspec \
--format progress \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress \
$TEST_FILES
# collect reports
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
exclude_patterns:
- "rdoc/*"
- "spec/*"
\ No newline at end of file
source "http://rubygems.org"
source 'http://rubygems.org'
# Dependencies required to run this gem.
gem "nokogiri", ">= 1.4.4"
gem "rubyzip", ">= 1.1.6" , :require => 'zip'
gem 'nokogiri', '>= 1.10.8'
gem 'rubyzip', '>= 1.3.0' , :require => 'zip'
# Development dependencies.
group :development do
gem "bundler"
gem "rake"
gem "jeweler"
# gem "simplecov", ">= 0"
gem "rspec", ">= 1.3.4"
group :development, :test do
gem 'bundler'
gem 'rake'
gem 'juwelier'
gem 'rspec'
gem 'simplecov'
# gem 'stackprof'
gem 'ruby-prof'
gem 'rspec_junit_formatter'
end
GEM
remote: http://rubygems.org/
specs:
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
builder (3.2.4)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
diff-lcs (1.5.0)
docile (1.4.0)
faraday (1.10.0)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.0)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.0.3)
multipart-post (>= 1.2, < 3)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.3)
git (1.11.0)
rchardet (~> 1.8)
github_api (0.19.0)
addressable (~> 2.4)
descendants_tracker (~> 0.0.4)
faraday (>= 0.8, < 2)
hashie (~> 3.5, >= 3.5.2)
oauth2 (~> 1.0)
hashie (3.6.0)
highline (2.0.3)
juwelier (2.4.9)
builder
bundler
git
github_api
highline
kamelcase (~> 0)
nokogiri
psych
rake
rdoc
semver2
jwt (2.3.0)
kamelcase (0.0.2)
semver2 (~> 3)
mini_portile2 (2.8.0)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
nokogiri (1.13.6)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
oauth2 (1.4.9)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
psych (4.0.4)
stringio
public_suffix (4.0.7)
racc (1.6.0)
rack (2.2.3)
rake (13.0.6)
rchardet (1.8.0)
rdoc (6.4.0)
psych (>= 4.0.0)
rspec (3.11.0)
rspec-core (~> 3.11.0)
rspec-expectations (~> 3.11.0)
rspec-mocks (~> 3.11.0)
rspec-core (3.11.0)
rspec-support (~> 3.11.0)
rspec-expectations (3.11.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-mocks (3.11.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-support (3.11.0)
rspec_junit_formatter (0.5.1)
rspec-core (>= 2, < 4, != 2.12.0)
ruby-prof (1.4.3)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
semver2 (3.4.2)
simplecov (0.21.2)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.4)
stringio (3.0.2)
thread_safe (0.3.6)
PLATFORMS
ruby
DEPENDENCIES
bundler
juwelier
nokogiri (>= 1.10.8)
rake
rspec
rspec_junit_formatter
ruby-prof
rubyzip (>= 1.3.0)
simplecov
BUNDLED WITH
2.3.5
Copyright (c) 2011 Vivek Bhagwat, 2013-2014 Wesha
Copyright (c) 2011 Vivek Bhagwat, 2013-2020 Wesha
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
......
= rubyXL
{<img src="https://badge.fury.io/rb/rubyXL.svg" alt="Gem Version" />}[http://badge.fury.io/rb/rubyXL]
{<img src="https://codeclimate.com/github/weshatheleopard/rubyXL.png" alt="Code Climate" />}[https://codeclimate.com/github/weshatheleopard/rubyXL]
{<img src="https://circleci.com/gh/weshatheleopard/rubyXL.svg?style=svg" alt="CircleCI" />}[https://circleci.com/gh/weshatheleopard/rubyXL]
This gem supports operating on +xlsx+ files (Open XML format). While it is capable
of properly parsing the entire OOXML structure, its current main emphasis is on
reading files produced by MS Excel, making minor modifications to them and saving
them to be opened again, while preserving as much of the structure as possible.
Please note that proprietary binary +xls+ format is *not* supported.
Please note that proprietary binary +xls+ format is *not* supported by this gem. If you need to parse those files, try {spreadsheet}[https://github.com/zdavatz/spreadsheet] gem.
== To Install:
gem install rubyXL
......@@ -15,6 +16,18 @@ Please note that proprietary binary +xls+ format is *not* supported.
== To Use:
require 'rubyXL' # Assuming rubygems is already required
=== Convenience methods
Starting with version 3.4.0, the main data structure has been separated from the convenience methods that provide access to individual features of the +xlsx+ format, in order to decrease the memory footprint. If you intend to use these features, you will need to additionally include the respective files:
require 'rubyXL/convenience_methods/cell'
require 'rubyXL/convenience_methods/color'
require 'rubyXL/convenience_methods/font'
require 'rubyXL/convenience_methods/workbook'
require 'rubyXL/convenience_methods/worksheet'
If you do not care about your RAM usage, just include them all at once by adding the following line to your code so it can continue operating just as before:
require 'rubyXL/convenience_methods'
=== Parsing an existing workbook
workbook = RubyXL::Parser.parse("path/to/Excel/file.xlsx")
......@@ -26,22 +39,30 @@ Please note that proprietary binary +xls+ format is *not* supported.
==== Accessing a Worksheet
workbook.worksheets[0] # Returns first worksheet
workbook[0] # Returns first worksheet
workbook['Sheet1'] # Finds and returns worksheet titled "Sheet1"
==== Accessing just the values
worksheet = workbook[0]
worksheet.extract_data # Produces a simple rectangular array that consists only of cell values (rather than the Cell objects)
workbook['Sheet1'] # Finds and returns worksheet titled "Sheet1". Note that sheet names in Excel are limited to 31 character.
==== Accessing a Row (Array of Cells)
Please note that worksheet is a _sparse_ array of rows. Your code *must* expect that any row it plucks from the array may be <tt>nil</tt>.
worksheet = workbook[0]
worksheet.sheet_data[0] # Returns first row of the worksheet
worksheet[0] # Returns first row of the worksheet
==== Accessing a Cell object
Please note that row is a _sparse_ array of cells. Your code *must* expect that any cell it plucks from the array may be <tt>nil</tt>.
worksheet = workbook[0]
worksheet.sheet_data[0][0] # Returns cell A1 in the worksheet
worksheet[0][0] # Returns cell A1 in the worksheet
cell = worksheet[0][0]
cell.value # Returns a properly converted value in the cell (if the file claims that the cell
# is holding a number, returns a respective Integer or Float, and so on).
Or, if you prefer Excel-style references (single-cell only!)
cell = worksheet.cell_at('B11')
==== Wrappers for accessing Cell properties
cell = workbook[0][0][0]
cell.is_struckthrough # Returns +true+ if the cell is struckthrough, other boolean properties have same syntax
......@@ -51,7 +72,9 @@ Please note that proprietary binary +xls+ format is *not* supported.
cell.fill_color
cell.horizontal_alignment
cell.vertical_alignment
cell.border_top
cell.get_border(:top)
cell.get_border_color(:top)
cell.text_rotation
==== Wrappers for accessing Row properties
Please note: these methods are being phased out in favor of the OOXML object model.
......@@ -62,9 +85,10 @@ Please note: these methods are being phased out in favor of the OOXML object mod
worksheet.get_row_font_color(0)
worksheet.is_row_underlined(0)
worksheet.get_row_height(0)
worksheet.get_row_horizontal_alignment(0)
worksheet.get_row_vertical_alignment(0)
worksheet.get_row_border_right(0)
worksheet.get_row_alignment(0, true)
worksheet.get_row_alignment(0, false)
worksheet.get_row_border(0, :right)
worksheet.get_row_border_color(0, :right)
==== Accessing column properties
Please note: these methods are being phased out in favor of the OOXML object model.
......@@ -75,24 +99,10 @@ Please note: these methods are being phased out in favor of the OOXML object mod
worksheet.get_column_font_color(0)
worksheet.is_column_underlined(0)
worksheet.get_column_width(0)
worksheet.get_column_horizontal_alignment(0)
worksheet.get_column_vertical_alignment(0)
worksheet.get_column_border_right(0)
==== Table identification
worksheet = workbook[0]
worksheet.get_table(["NAME", "AGE", "HEIGHT"]) # Returns hash of a table in the first worksheet, with the specified strings as headers, accessible by row and column
#it returns the following structure
{
:Name=>["John", "Jane", "Joe"],
:Height=>[70, 65, 68],
:Age=>[30, 25, 35]
:table=>[
{:Name=>"John", :Height=>70, :Age=>30},
{:Name=>"Jane", :Height=>65, :Age=>25},
{:Name=>"Joe", :Height=>68, :Age=>35}
]
}
worksheet.get_column_alignment(0, :horizontal)
worksheet.get_column_alignment(0, :vertical)
worksheet.get_column_border(0, :right)
worksheet.get_column_border_color(0, :right)
=== Modifying
......@@ -100,7 +110,7 @@ Please note: these methods are being phased out in favor of the OOXML object mod
worksheet = workbook.add_worksheet('Sheet2')
==== Renaming Worksheets
worksheet.sheet_name = 'Cool New Name'
worksheet.sheet_name = 'Cool New Name' # Note that sheet name is limited to 31 characters by Excel.
==== Adding Cells
worksheet.add_cell(0, 0, 'A1') # Sets cell A1 to string "A1"
......@@ -126,18 +136,30 @@ Please note: these methods are being phased out in favor of the OOXML object mod
worksheet.change_row_border(0, :left, 'hairline') # Sets first row to have a left, hairline border
worksheet.change_column_border(0, :diagonal, 'medium') # Sets first column to have diagonal, medium border
# Set the border style first so there's something to color.
worksheet.change_row_border_color(0, :top, '0ba53d') # Sets first row to have a green top border
worksheet.change_column_border_color(0, :top, '0ba53d') # Sets first column to have a green top border
==== Changing Alignment
===== Horizontal
center, distributed, justify, left, right
# Possible alignments: center, distributed, justify, left, right
worksheet.sheet_data[0][0].change_horizontal_alignment('center') # Sets A1 to be centered
worksheet.change_row_horizontal_alignment(0, 'justify') # Sets first row to be justified
worksheet.change_column_horizontal_alignment(0, 'right') # Sets first column to be right-aligned
===== Vertical
bottom, center, distributed, top
# Possible alignments: bottom, center, distributed, top
worksheet.sheet_data[0][0].change_vertical_alignment('bottom') # Sets A1 to be bottom aligned
worksheet.change_row_vertical_alignment(0, 'distributed') # Sets first row to be distributed vertically
worksheet.change_column_vertical_alignment(0, 'top') # Sets first column to be top aligned
===== Rotation
# Possible values:
# * 0-90 - degrees counterclockwise, around the bottom LEFT corner of the cell;
# * 91-179 - degrees clockwise, around the bottom RIGHT corner of the cell;
# * 180-254 - degrees clockwise, around the bottom LEFT corner of the cell, text becomes progressively invisible
# * 255 - text is in normal rotation but displayed vertically (one letter under another), line feed starts new line to the right of the previous.
worksheet.sheet_data[0][0].change_text_rotation(90) # Sets A1 to be rotated by 90 degrees
==== Changing Row Height
worksheet.change_row_height(0, 30) # Sets first row height to 30
......@@ -188,19 +210,41 @@ WARNING: Use of this method WILL break formulas referencing cells which have bee
worksheet.delete_cell(0, 0, :up) # Deletes A1, shifts contents of first column up
worksheet.delete_cell(0, 0) # Deletes A1, does not shift cells
==== Modifying Cell Format
cell = worksheet[0][0]
cell.set_number_format '0.0000%' # For formats, see https://support.office.com/en-us/article/5026bbd6-04bc-48cd-bf33-80f18b4eae68
cell.change_text_wrap(true) # Makes the text in the cell to wrap.
cell.change_shrink_to_fit(true) # Makes the text in the cell to shrink to fit.
cell.change_text_indent(1) # Indents the text in the cell by 1 level
==== Add hyperlink to a Cell
cell.add_hyperlink('http://example.com')
cell.add_hyperlink('http://example.com', 'Some tooltip text')
== I/O
=== Writing
By default, the gem operates with files on the local filesystem:
workbook = RubyXL::Parser.parse("path/to/Excel/file.xlsx")
workbook.write("path/to/desired/Excel/file.xlsx")
=== Streaming
The gem can provide +StringIO+ object containing the resulting +xlsx+ file, eliminating the need to save it to disk first. This capability comes in handy for web servers.
It can also operate on +StringIO+ objects, thus eliminating the need to save the +xlsx+ file to disk. This capability comes in handy for web servers.
workbook = RubyXL::Parser.parse_buffer(buffer)
workbook.stream
== Miscellaneous
Reference.ind2ref(0,0) == 'A1' # Converts row and column index to Excel-style cell reference
Reference.ref2ind('A1') == [0, 0] # Converts Excel-style cell reference to row and column index
RubyXL::Reference.ind2ref(0,0) == 'A1' # Converts row and column index to Excel-style cell reference
RubyXL::Reference.ref2ind('A1') == [0, 0] # Converts Excel-style cell reference to row and column index
=== Suppress warnings about malformed input files
RubyXL.class_variable_set(:@@suppress_warnings, true)
== Data validation (colloquially referred to as "dropdown list")
worksheet.add_validation_list("A1", [ "value1", "value2" ])
== For more information
Take a look at the files in spec/lib/ for rspecs on most methods
......@@ -217,5 +261,5 @@ Take a look at the files in spec/lib/ for rspecs on most methods
== Copyright
Copyright (c) 2011 Vivek Bhagwat, 2013-2014 Wesha.
Copyright (c) 2011 Vivek Bhagwat, 2013-2022 Wesha.
See LICENSE.txt for further details.
# encoding: utf-8
require 'rubygems'
require 'bundler'
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
warn e.message
warn 'Run `bundle install` to install missing gems'
exit e.status_code
end
require 'rake'
require 'jeweler'
Jeweler::Tasks.new do |gem|
require 'juwelier'
Juwelier::Tasks.new do |gem|
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
gem.name = "rubyXL"
gem.homepage = "http://github.com/gilt/rubyXL"
gem.license = "MIT"
gem.summary = %Q{rubyXL is a gem which allows the parsing, creation, and manipulation of Microsoft Excel (.xlsx/.xlsm) Documents}
gem.description = %Q{rubyXL is a gem which allows the parsing, creation, and manipulation of Microsoft Excel (.xlsx/.xlsm) Documents}
gem.email = "bhagwat.vivek@gmail.com"
gem.authors = ["Vivek Bhagwat"]
gem.name = 'rubyXL'
gem.homepage = 'http://github.com/gilt/rubyXL'
gem.license = 'MIT'
gem.summary = %q{rubyXL is a gem which allows the parsing, creation, and manipulation of Microsoft Excel (.xlsx/.xlsm) Documents}
gem.description = %q{rubyXL is a gem which allows the parsing, creation, and manipulation of Microsoft Excel (.xlsx/.xlsm) Documents}
gem.email = 'bhagwat.vivek@gmail.com'
gem.authors = ['Vivek Bhagwat', 'Wesha']
# gem.required_ruby_version = '>2.1'
# dependencies defined in Gemfile
end
Jeweler::RubygemsDotOrgTasks.new
Juwelier::RubygemsDotOrgTasks.new
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/test_*.rb'
test.verbose = true
test.warning = true
end
=begin
require 'rcov/rcovtask'
Rcov::RcovTask.new do |test|
test.libs << 'test'
test.pattern = 'test/**/test_*.rb'
test.verbose = true
test.rcov_opts << '--exclude "gems/*"'
end
=end
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
RSpec::Core::RakeTask.new(:rspec)
task :default => :rspec
require 'rdoc/task'
Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
version = File.exist?('VERSION') ? File.read('VERSION') : ''
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "rubyXL #{version}"
......@@ -56,42 +46,42 @@ Rake::RDocTask.new do |rdoc|
rdoc.rdoc_files.include('lib/**/*.rb')
end
desc "Dump profiling data"
task :profile do
desc 'Dump profiling data with stackprof'
task :stackprof do
require 'benchmark'
require 'stackprof'
$:.unshift File.dirname(__FILE__) + '/lib' # Make Ruby aware of load path
$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib' # Make Ruby aware of load path
require './lib/rubyXL'
spreadsheets = Dir.glob(File.join("test", "input", "*.xls?")).sort!
spreadsheets = Dir.glob(File.join('test', 'input', '*.xls?')).sort!
spreadsheets.each { |input|
puts "<<<--- Profiling parsing of #{input}..."
doc = nil
StackProf.run(:mode => :cpu, :interval => 100,
:out => "tmp/stackprof-cpu-parse-#{File.basename(input)}.dump") {
StackProf.run(:mode => :cpu, :interval => 100,
:out => "tmp/stackprof-cpu-parse-#{File.basename(input)}.dump") {
doc = RubyXL::Parser.parse(input)
}
output = File.join("test", "output", File.basename(input))
puts "--->>> Profiling writing of #{output}..."
StackProf.run(:mode => :cpu, :interval => 100,
output = File.join('test', 'output', File.basename(input))
puts "--->>> Profiling writing of #{output}..."
StackProf.run(:mode => :cpu, :interval => 100,
:out => "tmp/stackprof-cpu-write-#{File.basename(input)}.dump") {
doc.write(output)
}
}
end
desc "Dump profiling data 2"
task :prof do
desc 'Dump profiling data with ruby-prof'
task :rubyprof do
require 'benchmark'
require 'ruby-prof'
$:.unshift File.dirname(__FILE__) + '/lib' # Make Ruby aware of load path
$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib' # Make Ruby aware of load path
require './lib/rubyXL'
spreadsheets = Dir.glob(File.join("test", "input", "*.xls?")).sort!
spreadsheets = Dir.glob(File.join('test', 'input', '*.xls?')).sort!
spreadsheets.each { |input|
puts "<<<--- Profiling parsing of #{input}..."
......@@ -102,8 +92,8 @@ task :prof do
printer = RubyProf::CallStackPrinter.new(result)
File.open("tmp/ruby-prof-parse-#{File.basename(input)}.html", 'w') { |f| printer.print(f, {}) }
output = File.join("test", "output", File.basename(input))
puts "--->>> Profiling writing of #{output}..."
output = File.join('test', 'output', File.basename(input))
puts "--->>> Profiling writing of #{output}..."
result = RubyProf.profile {
doc.write(output)
}
......
3.3.8
\ No newline at end of file
3.4.25
\ No newline at end of file
......@@ -2,10 +2,11 @@ require 'rubyXL/objects/root'
require 'rubyXL/parser'
module RubyXL
@@suppress_warnings = false
# Convert any path passed to absolute path (within the XLSX file).
def self.from_root(path)
return path unless path.absolute?
path.relative_path_from(OOXMLTopLevelObject::ROOT)
end
end
module RubyXL
# http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.cellvalues(v=office.14).aspx
module DataType
SHARED_STRING = 's'
RAW_STRING = 'str'
INLINE_STRING = 'inlineStr'
ERROR = 'e'
BOOLEAN = 'b'
NUMBER = 'n'
DATE = 'd' # Only available in Office2010.
end
module LegacyCell
attr_accessor :formula, :worksheet
def workbook
@worksheet.workbook
end
# changes fill color of cell
def change_fill(rgb = 'ffffff')
validate_worksheet
Color.validate_color(rgb)
self.style_index = workbook.modify_fill(self.style_index, rgb)
end
# Changes font name of cell
def change_font_name(new_font_name = 'Verdana')
validate_worksheet
font = get_cell_font.dup
font.set_name(new_font_name)
update_font_references(font)
end
# Changes font size of cell
def change_font_size(font_size = 10)
validate_worksheet
raise 'Argument must be a number' unless font_size.is_a?(Integer) || font_size.is_a?(Float)
font = get_cell_font.dup
font.set_size(font_size)