sheets 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +19 -0
- data/README.md +92 -0
- data/Rakefile +7 -0
- data/lib/sheets.rb +21 -0
- data/lib/sheets/base.rb +28 -0
- data/lib/sheets/parseable.rb +39 -0
- data/lib/sheets/parsers/base.rb +23 -0
- data/lib/sheets/parsers/csv_parser.rb +9 -0
- data/lib/sheets/parsers/roo_parser.rb +28 -0
- data/lib/sheets/renderable.rb +36 -0
- data/lib/sheets/renderers/base.rb +18 -0
- data/lib/sheets/renderers/csv_renderer.rb +7 -0
- data/lib/sheets/renderers/excel_renderer.rb +22 -0
- data/lib/sheets/version.rb +3 -0
- data/sheets.gemspec +23 -0
- data/test/base_test.rb +19 -0
- data/test/data/simple.csv +11 -0
- data/test/data/simple.ods +0 -0
- data/test/data/simple.xls +0 -0
- data/test/data/simple.xlsx +0 -0
- data/test/generators/test_classes.rb +7 -0
- data/test/parseable_test.rb +9 -0
- data/test/parsers/basic_parsers_test.rb +18 -0
- data/test/parsers/csv_parser_test.rb +37 -0
- data/test/renderable_test.rb +8 -0
- data/test/renderers/basic_renderers_test.rb +18 -0
- data/test/renderers/csv_renderer_test.rb +25 -0
- data/test/renderers/excel_renderer_test.rb +42 -0
- data/test/test_helper.rb +6 -0
- metadata +126 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2011 Bradley J. Spaulding
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
Sheets
|
2
|
+
==========
|
3
|
+
|
4
|
+
Sheets is a Facade on top of many spreadsheet formats, presenting them as simple, unified, native ruby arrays. It is intended to allow applications to easily import data from a wide variety of spreadsheet formats.
|
5
|
+
|
6
|
+
With Sheets, all cell values are strings representing the final, evaluated value of the cell.
|
7
|
+
|
8
|
+
This does mean that, in some cases, you will be casting data back into its native format.
|
9
|
+
|
10
|
+
However, this eliminates the need to deal with multiple spreadsheet formats and normalize data types in your application logic.
|
11
|
+
|
12
|
+
Your application only needs to care about the layout of the spreadsheet, and the format *you* want the data in.
|
13
|
+
|
14
|
+
Usage
|
15
|
+
----------
|
16
|
+
|
17
|
+
To retrieve a list of parseable spreadsheet formats at runtime:
|
18
|
+
|
19
|
+
Sheets::Base.parseable_formats # => ["csv", "xls", "xlsx", "ods"]
|
20
|
+
|
21
|
+
To open a spreadsheet, pass initialize Sheets::Base.new either a file path:
|
22
|
+
|
23
|
+
Sheets::Base.new( '/path/to/a/spreadsheet.(format)' )
|
24
|
+
|
25
|
+
or a file handle:
|
26
|
+
|
27
|
+
Sheets::Base.new( File.open('/path/to/a/spreadsheet.(format)') )
|
28
|
+
|
29
|
+
By default, Sheets will use the basename of the file to detect the spreadsheet type. You can override this by passing in the :format option:
|
30
|
+
|
31
|
+
*This is necessary if you pass Sheets an IO object, like StringIO, that doesn't have metadata like a filename/path.*
|
32
|
+
|
33
|
+
Sheets::Base.new( an_io_object_with_spreadsheet_data, :format => :xls )
|
34
|
+
|
35
|
+
Once you have imported a sheet, you can either grab the array:
|
36
|
+
|
37
|
+
sheet = Sheets::Base.new( # ... )
|
38
|
+
sheet.to_array
|
39
|
+
|
40
|
+
or utilize any of the Enumerable functions on the sheet:
|
41
|
+
|
42
|
+
sheet = Sheets::Base.new( # ... )
|
43
|
+
sheet.collect {|row| puts row }
|
44
|
+
|
45
|
+
Additionally, you may output the sheet in any of the renderable formats:
|
46
|
+
|
47
|
+
Sheets::Base.renderable_formats # => ['csv', 'xls']
|
48
|
+
sheet = Sheets::Base.new( file )
|
49
|
+
sheet.to_csv
|
50
|
+
sheet.to_xls
|
51
|
+
|
52
|
+
Sheets::Base will skip the parsing phase if initialized with an array, allowing you to render arrays to a native spreadsheet format:
|
53
|
+
|
54
|
+
my_awesome_data = [ ["Date", "Spent", "Earned"], ["2011-04-11", "$0.00", "$5,000.00"] ]
|
55
|
+
sheet = Sheets::Base.new( my_awesome_data )
|
56
|
+
sheet.to_csv
|
57
|
+
sheet.to_xls
|
58
|
+
|
59
|
+
Adding Parsers
|
60
|
+
------------
|
61
|
+
|
62
|
+
Parsers subclass Sheets::Parsers::Base, live in the Sheets::Parsers namespace and should respond to two methods:
|
63
|
+
|
64
|
+
* formats: returns an array of string format names (file extensions) that this parser class supports
|
65
|
+
* to_array: returns a simple array representation of the spreadsheet.
|
66
|
+
|
67
|
+
Parsers have access to @data and @format in order to do their parsing. See lib/sheets/parsers/* for examples.
|
68
|
+
|
69
|
+
Adding Renderers
|
70
|
+
------------
|
71
|
+
|
72
|
+
Renderers subclass Sheets::Renderers::Base, live in the Sheets::Renderers namespace and should respond to:
|
73
|
+
|
74
|
+
* formats: returns an array of string format names that this parser class supports
|
75
|
+
* to\_#{format}: For each format that a renderer supports, it should respond to "to\_#{format}", returning the file data of that format.
|
76
|
+
|
77
|
+
Renderers are given access to the results of Sheets::Base#to_array as @data. See lib/sheets/renderers/* for examples.
|
78
|
+
|
79
|
+
License
|
80
|
+
----------
|
81
|
+
|
82
|
+
Sheets is licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
|
83
|
+
|
84
|
+
Please note that Sheets is dependent upon the Spreadsheet gem, which is licensed under the [GPLv3](http://www.opensource.org/licenses/gpl-3.0.html).
|
85
|
+
|
86
|
+
Credits
|
87
|
+
----------
|
88
|
+
|
89
|
+
Sheets takes full advantage of the great work done in these gems:
|
90
|
+
|
91
|
+
* [Spreadsheet](http://spreadsheet.rubyforge.org/) - mhatakeyama@ywesee.com, zdavatz@ywesee.com
|
92
|
+
* [Roo](http://roo.rubyforge.org/) - [Thomas Preymesser](mailto:thopre@gmail.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
task :test do
|
5
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), 'test', 'test_helper.rb')
|
6
|
+
Dir[ File.join(File.expand_path(File.dirname(__FILE__)), 'test', '**', '*_test.rb') ].each {|file| require file }
|
7
|
+
end
|
data/lib/sheets.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sheets
|
2
|
+
module Parsers; end
|
3
|
+
module Renderers; end
|
4
|
+
|
5
|
+
class UnsupportedSpreadsheetFormatError < StandardError; end
|
6
|
+
end
|
7
|
+
|
8
|
+
lib_path = File.expand_path(File.dirname(__FILE__))
|
9
|
+
|
10
|
+
# Load Parsers
|
11
|
+
require File.join lib_path, 'sheets', 'parseable.rb'
|
12
|
+
require File.join lib_path, 'sheets', 'parsers', 'base.rb'
|
13
|
+
Dir[ File.join lib_path, 'sheets', 'parsers', '*_parser.rb' ].each {|file| require file }
|
14
|
+
|
15
|
+
# Load Renderers
|
16
|
+
require File.join lib_path, 'sheets', 'renderable.rb'
|
17
|
+
require File.join lib_path, 'sheets', 'renderers', 'base.rb'
|
18
|
+
Dir[ File.join lib_path, 'sheets', 'renderers', '*_renderer.rb' ].each {|file| require file }
|
19
|
+
|
20
|
+
# Load Sheets::Base
|
21
|
+
require File.join lib_path, 'sheets', 'base.rb'
|
data/lib/sheets/base.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Sheets
|
2
|
+
class Base
|
3
|
+
include Parseable
|
4
|
+
include Renderable
|
5
|
+
|
6
|
+
def initialize(file, options = {})
|
7
|
+
if file.is_a?(Array)
|
8
|
+
@data = file
|
9
|
+
@extension = options[:format].to_s
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
file = File.open(file.to_s) unless file.respond_to? :read
|
14
|
+
options[:format] ||= File.basename(file.path || "").split('.')[-1]
|
15
|
+
|
16
|
+
@data = file.read
|
17
|
+
@extension = options[:format].to_s
|
18
|
+
@file_path = File.expand_path(file.path || "")
|
19
|
+
|
20
|
+
raise UnsupportedSpreadsheetFormatError, "Couldn't find a parser for the '#{@extension}' format." if parser.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :default_to_array, :to_array
|
24
|
+
def to_array
|
25
|
+
@data.is_a?(Array) ? @data : default_to_array
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Sheets
|
2
|
+
module Parseable
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def parseable_formats
|
11
|
+
Sheets::Parsers.constants.collect {|constant_name| Sheets::Parsers.const_get(constant_name) }.map(&:formats).flatten.uniq
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def each
|
16
|
+
to_array.each {|row| yield row }
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_array
|
20
|
+
parser.send(:to_array)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def parser_class
|
25
|
+
classes = Sheets::Parsers.constants.map do |constant_name|
|
26
|
+
constant = Sheets::Parsers.const_get(constant_name)
|
27
|
+
constant if constant && constant.respond_to?(:formats) && constant.formats.map(&:to_s).include?(@extension)
|
28
|
+
end
|
29
|
+
|
30
|
+
classes.delete(nil)
|
31
|
+
|
32
|
+
classes.first
|
33
|
+
end
|
34
|
+
|
35
|
+
def parser
|
36
|
+
@parser ||= parser_class.new(@data, @extension, @file_path) unless parser_class.nil?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Sheets::Parsers::Base
|
2
|
+
def initialize(data, format, file_path)
|
3
|
+
@data = data
|
4
|
+
@format = format
|
5
|
+
@file_path = file_path
|
6
|
+
end
|
7
|
+
|
8
|
+
def io
|
9
|
+
StringIO.new(@data)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.parses(*args)
|
13
|
+
self.formats = args.map(&:to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.formats
|
17
|
+
@formats ||= []
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.formats=(new_formats)
|
21
|
+
@formats = new_formats
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'roo'
|
2
|
+
|
3
|
+
class Sheets::Parsers::RooParser < Sheets::Parsers::Base
|
4
|
+
parses :xls, :xlsx, :ods
|
5
|
+
|
6
|
+
ROO_CLASS = {
|
7
|
+
:xls => Excel,
|
8
|
+
:xlsx => Excelx,
|
9
|
+
:ods => Openoffice
|
10
|
+
}
|
11
|
+
|
12
|
+
def to_array
|
13
|
+
array = []
|
14
|
+
(spreadsheet.first_row..spreadsheet.last_row).each do |row_num|
|
15
|
+
row = []
|
16
|
+
(spreadsheet.first_column..spreadsheet.last_column).each do |column_num|
|
17
|
+
row << spreadsheet.cell(row_num, column_num).to_s
|
18
|
+
end
|
19
|
+
array << row
|
20
|
+
end
|
21
|
+
array
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def spreadsheet
|
26
|
+
ROO_CLASS[@format.to_sym].new(@file_path)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Sheets
|
2
|
+
module Renderable
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def renderable_formats
|
9
|
+
Sheets::Renderers.constants.collect {|constant_name| Sheets::Renderers.const_get(constant_name) }.map(&:formats).flatten.uniq
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method_name, *args, &block)
|
14
|
+
match = method_name.to_s.match(/\Ato_(.*)/i)
|
15
|
+
format = match[1] unless match.nil?
|
16
|
+
|
17
|
+
format.nil? || !self.class.renderable_formats.include?(format) ? super(method_name, args, block) : renderer(format).send(method_name)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def renderer_class(format)
|
22
|
+
classes = Sheets::Renderers.constants.map do |constant_name|
|
23
|
+
constant = Sheets::Renderers.const_get(constant_name)
|
24
|
+
constant if constant && constant.respond_to?(:formats) && constant.formats.map(&:to_s).include?(format)
|
25
|
+
end
|
26
|
+
|
27
|
+
classes.delete(nil)
|
28
|
+
|
29
|
+
classes.first
|
30
|
+
end
|
31
|
+
|
32
|
+
def renderer(format = @extension)
|
33
|
+
@renderer ||= renderer_class(format).new(to_array, format) unless renderer_class(format).nil?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Sheets::Renderers::Base
|
2
|
+
def initialize(data, format)
|
3
|
+
@data = data
|
4
|
+
@format = format
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.renders(*args)
|
8
|
+
self.formats = args.map(&:to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.formats
|
12
|
+
@formats ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.formats=(new_formats)
|
16
|
+
@formats = new_formats
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Sheets::Renderers::ExcelRenderer < Sheets::Renderers::Base
|
2
|
+
renders :xls
|
3
|
+
|
4
|
+
def to_xls
|
5
|
+
workbook = Spreadsheet::Excel::Workbook.new
|
6
|
+
worksheet = Spreadsheet::Excel::Worksheet.new
|
7
|
+
workbook.add_worksheet(worksheet)
|
8
|
+
|
9
|
+
@data.each_with_index do |row, row_index|
|
10
|
+
row.each_with_index do |cell, col_index|
|
11
|
+
worksheet[row_index, col_index] = cell
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Tried to use StringIO here, but ran into encoding issues with Ruby 1.8.7.
|
16
|
+
file_path = "tmp_excel_render_#{Time.now.to_i}"
|
17
|
+
File.open(file_path, 'w+') {|file| workbook.write(file) }
|
18
|
+
File.read(file_path)
|
19
|
+
ensure
|
20
|
+
File.delete( file_path ) if File.exists?( file_path )
|
21
|
+
end
|
22
|
+
end
|
data/sheets.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "sheets/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "sheets"
|
7
|
+
s.version = Sheets::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Bradley J. Spaulding"]
|
10
|
+
s.email = ["brad.spaulding@gmail.com"]
|
11
|
+
s.homepage = "https://github.com/bspaulding/Sheets"
|
12
|
+
s.summary = %q{Sheets provides a Facade for importing spreadsheets that gives the application control. Any Spreadsheet can be represented as either (1) a two dimensional array, or (2) an array of hashes. Sheets' goal is to convert any spreadsheet format to one of these native Ruby data structures.}
|
13
|
+
s.description = %q{Work with spreadsheets easily in a native ruby format.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "sheets"
|
16
|
+
|
17
|
+
s.add_dependency('roo', '>= 1.9.3')
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
data/test/base_test.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class TestBase < Test::Unit::TestCase
|
2
|
+
EXAMPLE_DATA = [
|
3
|
+
["Date", "Impressions", "Clicks", "Actions"],
|
4
|
+
["2011-01-01", "10", "10", "10"],
|
5
|
+
["2011-01-02", "10", "10", "10"],
|
6
|
+
["2011-01-03", "10", "10", "10"],
|
7
|
+
["2011-01-04", "10", "10", "10"],
|
8
|
+
["2011-01-05", "10", "10", "10"],
|
9
|
+
["2011-01-06", "10", "10", "10"],
|
10
|
+
["2011-01-07", "10", "10", "10"],
|
11
|
+
["2011-01-08", "10", "10", "10"],
|
12
|
+
["2011-01-09", "10", "10", "10"],
|
13
|
+
["2011-01-10", "10", "10", "10"]
|
14
|
+
]
|
15
|
+
|
16
|
+
def test_initialize_with_array
|
17
|
+
assert_equal EXAMPLE_DATA, Sheets::Base.new( EXAMPLE_DATA ).to_array
|
18
|
+
end
|
19
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class TestParseable < Test::Unit::TestCase
|
2
|
+
Sheets::Base.parseable_formats.each do |format|
|
3
|
+
define_method "test_sheet_parses_#{format}" do
|
4
|
+
assert_nothing_raised do
|
5
|
+
Sheets::Base.new( File.join('test', 'data', "simple.#{format}") ).to_array
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
parser_classes = (Sheets::Parsers.constants - ["Base"]).map {|constant_name| Sheets::Parsers.const_get(constant_name) }
|
2
|
+
|
3
|
+
test_classes_for_collection parser_classes do |parser_class|
|
4
|
+
define_method :test_provides_formats do
|
5
|
+
assert parser_class.respond_to? :formats, "#{parser_class} doesn't respond_to formats."
|
6
|
+
end
|
7
|
+
|
8
|
+
define_method :test_formats_not_empty do
|
9
|
+
assert !parser_class.formats.empty?, "#{parser_class} doesn't render any formats."
|
10
|
+
end
|
11
|
+
|
12
|
+
parser_class.formats.each do |format|
|
13
|
+
define_method "test_#{format}_to_array" do
|
14
|
+
parser = parser_class.new([], format, nil)
|
15
|
+
assert parser.respond_to?("to_array"), "#{parser.inspect} doesn't respond to to_array"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class TestCSVParser < Test::Unit::TestCase
|
2
|
+
|
3
|
+
def test_to_array
|
4
|
+
file_path = generate_test_spreadsheet
|
5
|
+
assert_equal Sheets::Base.new( file_path ).to_array, example_spreadsheet_data
|
6
|
+
ensure
|
7
|
+
File.delete( file_path ) if File.exists?( file_path )
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
def example_spreadsheet_data
|
12
|
+
[
|
13
|
+
["Date", "Impressions", "Clicks", "Actions"],
|
14
|
+
["2011-01-01", "10", "10", "10"],
|
15
|
+
["2011-01-02", "10", "10", "10"],
|
16
|
+
["2011-01-03", "10", "10", "10"],
|
17
|
+
["2011-01-04", "10", "10", "10"],
|
18
|
+
["2011-01-05", "10", "10", "10"],
|
19
|
+
["2011-01-06", "10", "10", "10"],
|
20
|
+
["2011-01-07", "10", "10", "10"],
|
21
|
+
["2011-01-08", "10", "10", "10"],
|
22
|
+
["2011-01-09", "10", "10", "10"],
|
23
|
+
["2011-01-10", "10", "10", "10"]
|
24
|
+
]
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_test_spreadsheet
|
28
|
+
filename = "test_simple_#{Time.new.to_i}.csv"
|
29
|
+
file_path = File.join('.', filename)
|
30
|
+
|
31
|
+
File.open( file_path, 'w+' ) do |file|
|
32
|
+
file.write( example_spreadsheet_data.collect {|row| row.join(',') }.join("\n") )
|
33
|
+
end
|
34
|
+
|
35
|
+
file_path
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class TestRenderable < Test::Unit::TestCase
|
2
|
+
Sheets::Base.renderable_formats.each do |renderable_format|
|
3
|
+
define_method "test_sheet_responds_to_#{renderable_format}" do
|
4
|
+
sheet = Sheets::Base.new( File.join('test', 'data', 'simple.csv') )
|
5
|
+
assert_nothing_raised { sheet.send("to_#{renderable_format}") }
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
renderer_classes = (Sheets::Renderers.constants - ["Base"]).map {|constant_name| Sheets::Renderers.const_get(constant_name) }
|
2
|
+
|
3
|
+
test_classes_for_collection renderer_classes do |renderer_class|
|
4
|
+
define_method :test_provides_formats do
|
5
|
+
assert renderer_class.respond_to? :formats, "#{renderer_class} doesn't respond_to formats."
|
6
|
+
end
|
7
|
+
|
8
|
+
define_method :test_formats_not_empty do
|
9
|
+
assert !renderer_class.formats.empty?, "#{renderer_class} doesn't render any formats."
|
10
|
+
end
|
11
|
+
|
12
|
+
renderer_class.formats.each do |format|
|
13
|
+
define_method "test_to_#{format}" do
|
14
|
+
renderer = renderer_class.new([], format)
|
15
|
+
assert renderer.respond_to?("to_#{format}"), "#{renderer.inspect} doesn't respond to to_#{format}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class TestCSVRenderer < Test::Unit::TestCase
|
2
|
+
|
3
|
+
def test_to_csv
|
4
|
+
renderer = Sheets::Renderers::CSVRenderer.new(example_spreadsheet_data, :csv)
|
5
|
+
|
6
|
+
assert_equal renderer.to_csv, example_spreadsheet_data.collect {|row| row.join(',') }.join("\n")
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
def example_spreadsheet_data
|
11
|
+
[
|
12
|
+
["Date", "Impressions", "Clicks", "Actions"],
|
13
|
+
["2011-01-01", "10", "10", "10"],
|
14
|
+
["2011-01-02", "10", "10", "10"],
|
15
|
+
["2011-01-03", "10", "10", "10"],
|
16
|
+
["2011-01-04", "10", "10", "10"],
|
17
|
+
["2011-01-05", "10", "10", "10"],
|
18
|
+
["2011-01-06", "10", "10", "10"],
|
19
|
+
["2011-01-07", "10", "10", "10"],
|
20
|
+
["2011-01-08", "10", "10", "10"],
|
21
|
+
["2011-01-09", "10", "10", "10"],
|
22
|
+
["2011-01-10", "10", "10", "10"]
|
23
|
+
]
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class TestExcelRenderer < Test::Unit::TestCase
|
2
|
+
|
3
|
+
EXAMPLE_DATA = [
|
4
|
+
["Date", "Impressions", "Clicks", "Actions"],
|
5
|
+
["2011-01-01", "10", "10", "10"],
|
6
|
+
["2011-01-02", "10", "10", "10"],
|
7
|
+
["2011-01-03", "10", "10", "10"],
|
8
|
+
["2011-01-04", "10", "10", "10"],
|
9
|
+
["2011-01-05", "10", "10", "10"],
|
10
|
+
["2011-01-06", "10", "10", "10"],
|
11
|
+
["2011-01-07", "10", "10", "10"],
|
12
|
+
["2011-01-08", "10", "10", "10"],
|
13
|
+
["2011-01-09", "10", "10", "10"],
|
14
|
+
["2011-01-10", "10", "10", "10"]
|
15
|
+
]
|
16
|
+
|
17
|
+
def test_to_xls
|
18
|
+
renderer = Sheets::Renderers::ExcelRenderer.new(EXAMPLE_DATA, :xls)
|
19
|
+
|
20
|
+
file_path = File.expand_path( File.join('test', 'data', "excel_render_test_#{Time.now.to_i}.xls") )
|
21
|
+
File.open(file_path, 'w+') {|file| file.write(renderer.to_xls) }
|
22
|
+
|
23
|
+
assert_equal EXAMPLE_DATA, Sheets::Base.new( file_path ).to_array
|
24
|
+
ensure
|
25
|
+
File.delete( file_path ) if File.exists?( file_path )
|
26
|
+
end
|
27
|
+
|
28
|
+
Sheets::Base.parseable_formats.each do |parseable_format|
|
29
|
+
define_method "test_#{parseable_format}_to_xls" do
|
30
|
+
begin
|
31
|
+
sheet = Sheets::Base.new( File.join('test', 'data', "simple.#{parseable_format}") )
|
32
|
+
|
33
|
+
file_path = File.expand_path( File.join('test', 'data', "excel_render_test_#{Time.now.to_i}.xls") )
|
34
|
+
File.open(file_path, 'w+') {|file| file.write(sheet.to_xls) }
|
35
|
+
|
36
|
+
assert_equal sheet.to_array, Sheets::Base.new( file_path ).to_array
|
37
|
+
ensure
|
38
|
+
File.delete( file_path ) if File.exists?( file_path )
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
puts 'Loading Test Generators'
|
4
|
+
Dir[ File.join(File.expand_path(File.dirname(__FILE__)), 'generators', '*.rb') ].each {|file| puts "- #{file}"; require file }
|
5
|
+
|
6
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib', 'sheets.rb')
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sheets
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 59
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Bradley J. Spaulding
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-04-11 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: roo
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 53
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 9
|
33
|
+
- 3
|
34
|
+
version: 1.9.3
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
description: Work with spreadsheets easily in a native ruby format.
|
38
|
+
email:
|
39
|
+
- brad.spaulding@gmail.com
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- .gitignore
|
48
|
+
- Gemfile
|
49
|
+
- LICENSE.txt
|
50
|
+
- README.md
|
51
|
+
- Rakefile
|
52
|
+
- lib/sheets.rb
|
53
|
+
- lib/sheets/base.rb
|
54
|
+
- lib/sheets/parseable.rb
|
55
|
+
- lib/sheets/parsers/base.rb
|
56
|
+
- lib/sheets/parsers/csv_parser.rb
|
57
|
+
- lib/sheets/parsers/roo_parser.rb
|
58
|
+
- lib/sheets/renderable.rb
|
59
|
+
- lib/sheets/renderers/base.rb
|
60
|
+
- lib/sheets/renderers/csv_renderer.rb
|
61
|
+
- lib/sheets/renderers/excel_renderer.rb
|
62
|
+
- lib/sheets/version.rb
|
63
|
+
- sheets.gemspec
|
64
|
+
- test/base_test.rb
|
65
|
+
- test/data/simple.csv
|
66
|
+
- test/data/simple.ods
|
67
|
+
- test/data/simple.xls
|
68
|
+
- test/data/simple.xlsx
|
69
|
+
- test/generators/test_classes.rb
|
70
|
+
- test/parseable_test.rb
|
71
|
+
- test/parsers/basic_parsers_test.rb
|
72
|
+
- test/parsers/csv_parser_test.rb
|
73
|
+
- test/renderable_test.rb
|
74
|
+
- test/renderers/basic_renderers_test.rb
|
75
|
+
- test/renderers/csv_renderer_test.rb
|
76
|
+
- test/renderers/excel_renderer_test.rb
|
77
|
+
- test/test_helper.rb
|
78
|
+
has_rdoc: true
|
79
|
+
homepage: https://github.com/bspaulding/Sheets
|
80
|
+
licenses: []
|
81
|
+
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
hash: 3
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
version: "0"
|
105
|
+
requirements: []
|
106
|
+
|
107
|
+
rubyforge_project: sheets
|
108
|
+
rubygems_version: 1.5.0
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: Sheets provides a Facade for importing spreadsheets that gives the application control. Any Spreadsheet can be represented as either (1) a two dimensional array, or (2) an array of hashes. Sheets' goal is to convert any spreadsheet format to one of these native Ruby data structures.
|
112
|
+
test_files:
|
113
|
+
- test/base_test.rb
|
114
|
+
- test/data/simple.csv
|
115
|
+
- test/data/simple.ods
|
116
|
+
- test/data/simple.xls
|
117
|
+
- test/data/simple.xlsx
|
118
|
+
- test/generators/test_classes.rb
|
119
|
+
- test/parseable_test.rb
|
120
|
+
- test/parsers/basic_parsers_test.rb
|
121
|
+
- test/parsers/csv_parser_test.rb
|
122
|
+
- test/renderable_test.rb
|
123
|
+
- test/renderers/basic_renderers_test.rb
|
124
|
+
- test/renderers/csv_renderer_test.rb
|
125
|
+
- test/renderers/excel_renderer_test.rb
|
126
|
+
- test/test_helper.rb
|