fastsheet 0.0.7 → 0.1.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.
- checksums.yaml +4 -4
- data/.gitignore +14 -13
- data/README.md +24 -33
- data/Rakefile +11 -8
- data/bin/.keep +0 -0
- data/{Cargo.toml → ext/fastsheet/Cargo.toml} +3 -6
- data/ext/fastsheet/build.rs +62 -0
- data/ext/fastsheet/src/lib.rs +152 -0
- data/extconf.rb +28 -0
- data/fastsheet.gemspec +3 -8
- data/lib/fastsheet.rb +2 -5
- data/lib/fastsheet/sheet.rb +44 -0
- data/lib/fastsheet/version.rb +1 -1
- metadata +14 -42
- data/bin/benchmark +0 -59
- data/bin/console +0 -9
- data/lib/fastsheet/xlsx.rb +0 -4
- data/src/lib.rs +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6eb7e299b220f69ff05cb1dd1bb015409e4cc31
|
4
|
+
data.tar.gz: 972681080beecd7165e47d0e0ddda8305004e7a9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9155761df20a035bfa2e550600db4f5daa50813dac66b6558604649a1cb22b2d07db828c96ad300c5b7213eb5213a9d7ad0e1946dbc9b709b7a51c10b373beb3
|
7
|
+
data.tar.gz: 730e69d383da0b13e1dafc49ea2a9e0c287ad7ea56583e08fe01ebae58f89814cd20c24e32e908ba9638a1021017af02cdaa7c8e4e666f2e0a7b0cb172774380
|
data/.gitignore
CHANGED
@@ -1,13 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
Makefile
|
2
|
+
.yardoc
|
3
|
+
.bundle/
|
4
|
+
_yardoc/
|
5
|
+
coverage/
|
6
|
+
doc/
|
7
|
+
pkg/
|
8
|
+
spec/reports/
|
9
|
+
tmp/
|
10
|
+
**/target/
|
11
|
+
**/*.so
|
12
|
+
**/*.gem
|
13
|
+
**/*.log
|
14
|
+
**/*.lock
|
data/README.md
CHANGED
@@ -2,33 +2,10 @@
|
|
2
2
|
|
3
3
|
Fastest ruby gem for reading Excel documents.
|
4
4
|
|
5
|
-
Benchmark:
|
6
|
-
```shell
|
7
|
-
$ bin/benchmark
|
8
|
-
Benchmark reading row №2991 from `./first_file.xlsx` 15 times...
|
9
|
-
┌───────────┬───────────┐
|
10
|
-
│ Reader │ Real Time │
|
11
|
-
├───────────┼───────────┤
|
12
|
-
│ Roo │ 161.3275 │
|
13
|
-
├───────────┼───────────┤
|
14
|
-
│ Fastsheet │ 4.3765 │
|
15
|
-
└───────────┴───────────┘
|
16
|
-
Fastsheet is 37 times faster
|
17
|
-
|
18
|
-
$ bin/benchmark
|
19
|
-
Benchmark reading row №7146 from `./second_file.xlsx` 15 times...
|
20
|
-
┌───────────┬───────────┐
|
21
|
-
│ Reader │ Real Time │
|
22
|
-
├───────────┼───────────┤
|
23
|
-
│ Roo │ 897.4474 │
|
24
|
-
├───────────┼───────────┤
|
25
|
-
│ Fastsheet │ 19.0869 │
|
26
|
-
└───────────┴───────────┘
|
27
|
-
Fastsheet is 47 times faster
|
28
|
-
```
|
29
|
-
|
30
5
|
## Installation
|
31
6
|
|
7
|
+
**You should have rust installed to use this gem.**
|
8
|
+
|
32
9
|
Add this line to your application's Gemfile:
|
33
10
|
|
34
11
|
```ruby
|
@@ -45,24 +22,38 @@ Or install it yourself as:
|
|
45
22
|
|
46
23
|
## Usage
|
47
24
|
|
48
|
-
|
25
|
+
Open a sheet:
|
49
26
|
|
50
27
|
```ruby
|
51
28
|
require 'fastsheet'
|
52
29
|
|
53
|
-
sheet =
|
30
|
+
sheet = Fastsheet::Sheet.new('path/to/sheet.xlsx')
|
54
31
|
|
55
|
-
|
56
|
-
sheet.
|
32
|
+
# number of columns
|
33
|
+
sheet.width
|
34
|
+
|
35
|
+
# number of rows
|
36
|
+
sheet.height
|
57
37
|
```
|
58
38
|
|
59
|
-
|
39
|
+
Get rows or columns:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
sheet.rows
|
43
|
+
sheet.columns
|
44
|
+
|
45
|
+
sheet.row(42)
|
46
|
+
sheet.column(1)
|
47
|
+
```
|
60
48
|
|
61
|
-
|
49
|
+
Iterators (returns enumerator if no block given):
|
62
50
|
|
63
|
-
|
51
|
+
```ruby
|
52
|
+
sheet.each_row { |r| ... }
|
53
|
+
sheet.each_column { |c| ... }
|
54
|
+
```
|
64
55
|
|
65
|
-
|
56
|
+
That's all API for now. Feel free to [open an issue](http://github.com/dkkoval/fastsheet/issues) if you need more.
|
66
57
|
|
67
58
|
## Contributing
|
68
59
|
|
data/Rakefile
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bundler/gem_tasks'
|
4
|
-
require 'rake/clean'
|
5
|
-
require 'rake/testtask'
|
6
1
|
require 'bundler/setup'
|
7
|
-
require 'thermite/tasks'
|
8
2
|
|
9
|
-
|
3
|
+
desc 'Build native extension'
|
4
|
+
task :build do
|
5
|
+
ruby 'extconf.rb'
|
6
|
+
end
|
7
|
+
|
8
|
+
# TODO
|
9
|
+
desc 'Run tests'
|
10
|
+
task :test do
|
11
|
+
puts "no tests"
|
12
|
+
end
|
10
13
|
|
11
|
-
task default:
|
14
|
+
task default: [:build, :test]
|
data/bin/.keep
ADDED
File without changes
|
@@ -1,16 +1,13 @@
|
|
1
1
|
[package]
|
2
2
|
name = "fastsheet"
|
3
3
|
version = "0.1.0"
|
4
|
-
|
5
|
-
|
6
|
-
github_releases = true
|
7
|
-
github_release_type = "latest"
|
4
|
+
publish = false
|
5
|
+
build = "build.rs"
|
8
6
|
|
9
7
|
[lib]
|
10
8
|
name = "fastsheet"
|
11
9
|
crate-type = ["cdylib"]
|
12
10
|
|
13
11
|
[dependencies]
|
14
|
-
ruru = "0.9.3"
|
15
|
-
lazy_static = "0.2.1"
|
16
12
|
calamine = "0.11.8"
|
13
|
+
libc = "0.2"
|
@@ -0,0 +1,62 @@
|
|
1
|
+
use std::env;
|
2
|
+
use std::ffi::OsStr;
|
3
|
+
use std::process::Command;
|
4
|
+
|
5
|
+
fn rbconfig(key: &str) -> Vec<u8> {
|
6
|
+
let ruby = match env::var_os("RUBY") {
|
7
|
+
Some(val) => val.to_os_string(),
|
8
|
+
None => OsStr::new("ruby").to_os_string(),
|
9
|
+
};
|
10
|
+
let config = Command::new(ruby)
|
11
|
+
.arg("-e")
|
12
|
+
.arg(format!("print RbConfig::CONFIG['{}']", key))
|
13
|
+
.output()
|
14
|
+
.unwrap_or_else(|e| panic!("ruby not found: {}", e));
|
15
|
+
|
16
|
+
config.stdout
|
17
|
+
}
|
18
|
+
|
19
|
+
fn use_static() {
|
20
|
+
let ruby_libs = rbconfig("LIBS");
|
21
|
+
let libs = String::from_utf8_lossy(&ruby_libs);
|
22
|
+
|
23
|
+
// Ruby gives back the libs in the form: `-lpthread -lgmp`
|
24
|
+
// Cargo wants them as: `-l pthread -l gmp`
|
25
|
+
let transformed_lib_args = libs.replace("-l", "-l ");
|
26
|
+
|
27
|
+
println!("cargo:rustc-link-lib=static=ruby-static");
|
28
|
+
println!("cargo:rustc-flags={}", transformed_lib_args);
|
29
|
+
}
|
30
|
+
|
31
|
+
fn use_dylib(lib: Vec<u8>) {
|
32
|
+
println!("cargo:rustc-link-lib=dylib={}",
|
33
|
+
String::from_utf8_lossy(&lib));
|
34
|
+
}
|
35
|
+
|
36
|
+
fn main() {
|
37
|
+
let libdir = rbconfig("libdir");
|
38
|
+
|
39
|
+
let libruby_static = rbconfig("LIBRUBY_A");
|
40
|
+
let libruby_so = rbconfig("RUBY_SO_NAME");
|
41
|
+
|
42
|
+
match (libruby_static.is_empty(), libruby_so.is_empty()) {
|
43
|
+
(false, true) => use_static(),
|
44
|
+
(true, false) => use_dylib(libruby_so),
|
45
|
+
(false, false) => {
|
46
|
+
if env::var_os("RUBY_STATIC").is_some() {
|
47
|
+
use_static()
|
48
|
+
} else {
|
49
|
+
use_dylib(libruby_so)
|
50
|
+
}
|
51
|
+
},
|
52
|
+
_ => {
|
53
|
+
let msg = "Error! Could not find LIBRUBY_A or RUBY_SO_NAME. \
|
54
|
+
This means that no static, or dynamic libruby was found. \
|
55
|
+
Possible solution: build a new Ruby with the `--enable-shared` configure opt.";
|
56
|
+
panic!(msg)
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
println!("cargo:rustc-link-search={}",
|
61
|
+
String::from_utf8_lossy(&libdir));
|
62
|
+
}
|
@@ -0,0 +1,152 @@
|
|
1
|
+
extern crate libc;
|
2
|
+
extern crate calamine;
|
3
|
+
|
4
|
+
use std::ffi::{CString, CStr};
|
5
|
+
use libc::{c_int, c_void, c_char, c_double, uintptr_t};
|
6
|
+
|
7
|
+
use calamine::{Sheets, DataType};
|
8
|
+
|
9
|
+
//
|
10
|
+
// Prepare Ruby bindings
|
11
|
+
//
|
12
|
+
|
13
|
+
// VALUE (pointer to a ruby object)
|
14
|
+
type Value = uintptr_t;
|
15
|
+
|
16
|
+
// Some ruby constant values
|
17
|
+
const NIL: usize = 0x08;
|
18
|
+
const TRUE: usize = 0x14;
|
19
|
+
const FALSE: usize = 0x00;
|
20
|
+
|
21
|
+
// Load some Ruby API functions
|
22
|
+
extern "C" {
|
23
|
+
// Object class
|
24
|
+
static rb_cObject: Value;
|
25
|
+
|
26
|
+
// Modules and classes
|
27
|
+
fn rb_define_module(name: *const c_char) -> Value;
|
28
|
+
fn rb_define_class_under(outer: Value, name: *const c_char, superclass: Value) -> Value;
|
29
|
+
fn rb_define_method(class: Value, name: *const c_char, method: *const c_void, argc: c_int) -> Value;
|
30
|
+
|
31
|
+
// Set instance variables
|
32
|
+
fn rb_iv_set(object: Value, name: *const c_char, value: Value) -> Value;
|
33
|
+
|
34
|
+
// Array
|
35
|
+
fn rb_ary_new() -> Value;
|
36
|
+
fn rb_ary_push(array: Value, elem: Value) -> Value;
|
37
|
+
|
38
|
+
// C data to Ruby
|
39
|
+
fn rb_int2big(num: c_int) -> Value;
|
40
|
+
fn rb_float_new(num: c_double) -> Value;
|
41
|
+
fn rb_utf8_str_new_cstr(str: *const c_char) -> Value;
|
42
|
+
|
43
|
+
// Ruby string to C string
|
44
|
+
fn rb_string_value_cstr(str: *const Value) -> *const c_char;
|
45
|
+
}
|
46
|
+
|
47
|
+
//
|
48
|
+
// Utils
|
49
|
+
//
|
50
|
+
|
51
|
+
// C string from Rust string
|
52
|
+
pub fn cstr(string: &str) -> CString {
|
53
|
+
CString::new(string).unwrap()
|
54
|
+
}
|
55
|
+
|
56
|
+
// Rust string from Ruby string
|
57
|
+
pub fn rstr(string: Value) -> String {
|
58
|
+
unsafe {
|
59
|
+
let s = rb_string_value_cstr(&string);
|
60
|
+
CStr::from_ptr(s).to_string_lossy().into_owned()
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
//
|
65
|
+
// Functions to use in Ruby
|
66
|
+
//
|
67
|
+
|
68
|
+
// Read the sheet
|
69
|
+
unsafe fn read(this: Value, rb_file_name: Value) -> Value {
|
70
|
+
let mut document =
|
71
|
+
Sheets::open(rstr(rb_file_name))
|
72
|
+
.expect("Cannot open file!");
|
73
|
+
|
74
|
+
// Open first worksheet by default
|
75
|
+
//
|
76
|
+
// TODO: allow use different worksheets
|
77
|
+
let sheet = document.worksheet_range_by_index(0).unwrap();
|
78
|
+
|
79
|
+
let rows = rb_ary_new();
|
80
|
+
|
81
|
+
for row in sheet.rows() {
|
82
|
+
let new_row = rb_ary_new();
|
83
|
+
|
84
|
+
for (_, c) in row.iter().enumerate() {
|
85
|
+
rb_ary_push(
|
86
|
+
new_row,
|
87
|
+
match *c {
|
88
|
+
// vba error
|
89
|
+
DataType::Error(_) => NIL,
|
90
|
+
DataType::Empty => NIL,
|
91
|
+
DataType::Float(ref f) => rb_float_new(*f as c_double),
|
92
|
+
DataType::Int(ref i) => rb_int2big(*i as c_int),
|
93
|
+
DataType::Bool(ref b) => if *b { TRUE } else { FALSE },
|
94
|
+
DataType::String(ref s) => {
|
95
|
+
let st = s.trim();
|
96
|
+
if st.is_empty() {
|
97
|
+
NIL
|
98
|
+
} else {
|
99
|
+
rb_utf8_str_new_cstr(cstr(st).as_ptr())
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
);
|
104
|
+
}
|
105
|
+
|
106
|
+
rb_ary_push(rows, new_row);
|
107
|
+
}
|
108
|
+
|
109
|
+
|
110
|
+
// Set instance variables
|
111
|
+
rb_iv_set(
|
112
|
+
this,
|
113
|
+
cstr("@width").as_ptr(),
|
114
|
+
rb_int2big(sheet.width() as i32)
|
115
|
+
);
|
116
|
+
|
117
|
+
rb_iv_set(
|
118
|
+
this,
|
119
|
+
cstr("@height").as_ptr(),
|
120
|
+
rb_int2big(sheet.height() as i32)
|
121
|
+
);
|
122
|
+
|
123
|
+
rb_iv_set(
|
124
|
+
this,
|
125
|
+
cstr("@rows").as_ptr(),
|
126
|
+
rows
|
127
|
+
);
|
128
|
+
|
129
|
+
this
|
130
|
+
}
|
131
|
+
|
132
|
+
// Init_libfastsheet symbol is an entrypoint for the lib
|
133
|
+
//
|
134
|
+
// This function will be executed when we require the lib.
|
135
|
+
//
|
136
|
+
#[no_mangle]
|
137
|
+
#[allow(non_snake_case)]
|
138
|
+
pub unsafe extern fn Init_libfastsheet() {
|
139
|
+
let Fastsheet =
|
140
|
+
rb_define_module(cstr("Fastsheet").as_ptr());
|
141
|
+
|
142
|
+
let Sheet =
|
143
|
+
rb_define_class_under(Fastsheet, cstr("Sheet").as_ptr(), rb_cObject);
|
144
|
+
|
145
|
+
rb_define_method(
|
146
|
+
Sheet,
|
147
|
+
cstr("read!").as_ptr(),
|
148
|
+
// Rust function as pointer to C function
|
149
|
+
read as *const c_void,
|
150
|
+
1 as c_int
|
151
|
+
);
|
152
|
+
}
|
data/extconf.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
abort unless have_library 'ruby'
|
5
|
+
abort unless have_header 'ruby.h'
|
6
|
+
|
7
|
+
abort unless find_executable 'rustc'
|
8
|
+
abort unless cargo = find_executable(ENV.fetch('CARGO', 'cargo'))
|
9
|
+
|
10
|
+
target_file = 'libfastsheet.so'
|
11
|
+
|
12
|
+
target = File.join(__dir__, 'ext/fastsheet/target/release', target_file)
|
13
|
+
lib_dest = File.join(__dir__, 'lib/fastsheet', target_file)
|
14
|
+
|
15
|
+
# HACK: rubygems requires Makefile with tasks above
|
16
|
+
File.write 'Makefile', <<EOF
|
17
|
+
all:
|
18
|
+
install:
|
19
|
+
clean:
|
20
|
+
EOF
|
21
|
+
$makefile_created = true
|
22
|
+
|
23
|
+
Dir.chdir 'ext/fastsheet' do
|
24
|
+
when_writing 'Building fastsheet...' do
|
25
|
+
sh cargo, 'build', '--release'
|
26
|
+
cp target, lib_dest
|
27
|
+
end
|
28
|
+
end
|
data/fastsheet.gemspec
CHANGED
@@ -20,13 +20,8 @@ Gem::Specification.new do |spec|
|
|
20
20
|
end
|
21
21
|
spec.bindir = 'bin'
|
22
22
|
spec.require_paths = ['lib']
|
23
|
-
spec.extensions
|
23
|
+
spec.extensions = %w[extconf.rb]
|
24
24
|
|
25
|
-
spec.
|
26
|
-
|
27
|
-
spec.add_development_dependency 'pry', '~> 0.10.4'
|
28
|
-
|
29
|
-
# for pretty benchmark
|
30
|
-
spec.add_development_dependency 'tty-spinner', '~> 0.7.0'
|
31
|
-
spec.add_development_dependency 'tty-table', '~> 0.8.0'
|
25
|
+
spec.add_development_dependency 'rake', '~>12.0.0'
|
26
|
+
spec.add_development_dependency 'pry', '~>0.10.4'
|
32
27
|
end
|
data/lib/fastsheet.rb
CHANGED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Fastsheet
|
2
|
+
class Sheet
|
3
|
+
attr_reader :file_name,
|
4
|
+
:rows, :header,
|
5
|
+
:width, :height
|
6
|
+
|
7
|
+
def initialize(file_name, options = {})
|
8
|
+
# this method sets @rows, @height and @width
|
9
|
+
read!(file_name)
|
10
|
+
|
11
|
+
@header = @rows.shift if options[:header]
|
12
|
+
end
|
13
|
+
|
14
|
+
def row(n)
|
15
|
+
@rows[n]
|
16
|
+
end
|
17
|
+
|
18
|
+
def each_row
|
19
|
+
if block_given?
|
20
|
+
@rows.each { |r| yield r }
|
21
|
+
else
|
22
|
+
@rows.each
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def column(n)
|
27
|
+
@rows.map { |r| r[n] }
|
28
|
+
end
|
29
|
+
|
30
|
+
def columns
|
31
|
+
(0...@width).inject([]) do |cols, i|
|
32
|
+
cols.push column(i)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def each_column
|
37
|
+
if block_given?
|
38
|
+
columns.each { |c| yield c }
|
39
|
+
else
|
40
|
+
columns.each
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/fastsheet/version.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastsheet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitry Koval
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-09-
|
11
|
+
date: 2017-09-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: rake
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
20
|
-
type: :
|
19
|
+
version: 12.0.0
|
20
|
+
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
26
|
+
version: 12.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pry
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,56 +38,28 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.10.4
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: tty-spinner
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: 0.7.0
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: 0.7.0
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: tty-table
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 0.8.0
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 0.8.0
|
69
41
|
description: Fastest ruby gem for reading Excel documents.
|
70
42
|
email:
|
71
43
|
- dkoval@heliostech.fr
|
72
44
|
executables: []
|
73
45
|
extensions:
|
74
|
-
-
|
46
|
+
- extconf.rb
|
75
47
|
extra_rdoc_files: []
|
76
48
|
files:
|
77
49
|
- ".gitignore"
|
78
|
-
- Cargo.toml
|
79
50
|
- Gemfile
|
80
|
-
- Gemfile.lock
|
81
51
|
- LICENSE.txt
|
82
52
|
- README.md
|
83
53
|
- Rakefile
|
84
|
-
- bin
|
85
|
-
-
|
54
|
+
- bin/.keep
|
55
|
+
- ext/fastsheet/Cargo.toml
|
56
|
+
- ext/fastsheet/build.rs
|
57
|
+
- ext/fastsheet/src/lib.rs
|
58
|
+
- extconf.rb
|
86
59
|
- fastsheet.gemspec
|
87
60
|
- lib/fastsheet.rb
|
61
|
+
- lib/fastsheet/sheet.rb
|
88
62
|
- lib/fastsheet/version.rb
|
89
|
-
- lib/fastsheet/xlsx.rb
|
90
|
-
- src/lib.rs
|
91
63
|
homepage: https://github.com/dkkoval/fastsheet
|
92
64
|
licenses:
|
93
65
|
- MIT
|
@@ -108,7 +80,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
80
|
version: '0'
|
109
81
|
requirements: []
|
110
82
|
rubyforge_project:
|
111
|
-
rubygems_version: 2.
|
83
|
+
rubygems_version: 2.6.11
|
112
84
|
signing_key:
|
113
85
|
specification_version: 4
|
114
86
|
summary: Fast XLSX reader
|
data/bin/benchmark
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
lib = File.expand_path('../../lib', __FILE__)
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
|
6
|
-
require 'benchmark'
|
7
|
-
require 'tty-spinner'
|
8
|
-
require 'tty-table'
|
9
|
-
|
10
|
-
require 'fastsheet'
|
11
|
-
require 'roo'
|
12
|
-
|
13
|
-
FILE = './second_file.xlsx'.freeze
|
14
|
-
ROW = rand 9000
|
15
|
-
N = 15
|
16
|
-
|
17
|
-
options = {
|
18
|
-
hide_cursor: true,
|
19
|
-
format: :dots,
|
20
|
-
interval: 20,
|
21
|
-
clear: true
|
22
|
-
}
|
23
|
-
|
24
|
-
puts "Benchmark reading row №#{ROW} from `#{FILE}` #{N} times..."
|
25
|
-
|
26
|
-
roo_time = Benchmark.measure('Roo') do
|
27
|
-
spinner = TTY::Spinner.new('[:spinner] :progress', options)
|
28
|
-
spinner.auto_spin
|
29
|
-
N.times do |i|
|
30
|
-
spinner.update(progress: "Roo: #{i + 1}/#{N} sheets opened ...")
|
31
|
-
Roo::Excelx.new(FILE).sheet(0).row(ROW)
|
32
|
-
end
|
33
|
-
spinner.stop
|
34
|
-
end
|
35
|
-
|
36
|
-
fastsheet_time = Benchmark.measure('Fastsheet') do
|
37
|
-
spinner = TTY::Spinner.new('[:spinner] :progress', options)
|
38
|
-
spinner.auto_spin
|
39
|
-
N.times do |i|
|
40
|
-
spinner.update(progress: "Fastsheet: #{i + 1}/#{N} sheets opened ...")
|
41
|
-
Xlsx.new(FILE).rows[ROW]
|
42
|
-
end
|
43
|
-
spinner.stop
|
44
|
-
end
|
45
|
-
|
46
|
-
table = TTY::Table.new(header: ['Reader', 'Real Time'])
|
47
|
-
|
48
|
-
table << ['Roo', roo_time.real.round(4)]
|
49
|
-
table << ['Fastsheet', fastsheet_time.real.round(4)]
|
50
|
-
|
51
|
-
renderer = TTY::Table::Renderer::Unicode.new(table)
|
52
|
-
|
53
|
-
renderer.alignments = %i[center center]
|
54
|
-
renderer.padding = [0, 1, 0, 1]
|
55
|
-
renderer.border.separator = :each_row
|
56
|
-
|
57
|
-
puts renderer.render
|
58
|
-
|
59
|
-
puts "Fastsheet is #{(roo_time.real / fastsheet_time.real).round} times faster"
|
data/bin/console
DELETED
data/lib/fastsheet/xlsx.rb
DELETED
data/src/lib.rs
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
#[macro_use] extern crate ruru;
|
2
|
-
#[macro_use] extern crate lazy_static;
|
3
|
-
|
4
|
-
extern crate calamine;
|
5
|
-
|
6
|
-
use ruru::{Array, Class, RString, Float, Fixnum, Boolean, Object, AnyObject, NilClass};
|
7
|
-
|
8
|
-
use calamine::{Sheets, DataType};
|
9
|
-
|
10
|
-
pub struct Reader {
|
11
|
-
file: String,
|
12
|
-
rows: Array
|
13
|
-
}
|
14
|
-
|
15
|
-
impl Reader {
|
16
|
-
fn new(file: String) -> Self {
|
17
|
-
let mut this = Reader {
|
18
|
-
file: file,
|
19
|
-
rows: Array::new()
|
20
|
-
};
|
21
|
-
|
22
|
-
let sheet = Sheets::open(&this.file).unwrap().worksheet_range_by_index(0).unwrap();
|
23
|
-
|
24
|
-
for row in sheet.rows() {
|
25
|
-
let mut new_row = Array::with_capacity(sheet.width());
|
26
|
-
|
27
|
-
for (_, c) in row.iter().enumerate() {
|
28
|
-
match *c {
|
29
|
-
DataType::Error(_) => new_row.push(NilClass::new()),
|
30
|
-
DataType::String(ref s) => new_row.push(RString::new(s)),
|
31
|
-
DataType::Empty => new_row.push(NilClass::new()),
|
32
|
-
DataType::Float(ref f) => new_row.push(Float::new(*f)),
|
33
|
-
DataType::Int(ref i) => new_row.push(Fixnum::new(*i)),
|
34
|
-
DataType::Bool(ref b) => new_row.push(Boolean::new(*b))
|
35
|
-
};
|
36
|
-
}
|
37
|
-
|
38
|
-
this.rows.push(new_row);
|
39
|
-
}
|
40
|
-
this
|
41
|
-
}
|
42
|
-
|
43
|
-
fn get_rows(&self) -> AnyObject {
|
44
|
-
self.rows.to_any_object()
|
45
|
-
}
|
46
|
-
}
|
47
|
-
|
48
|
-
wrappable_struct!(Reader, ReaderWrapper, READER_WRAPPER);
|
49
|
-
|
50
|
-
class!(Xlsx);
|
51
|
-
|
52
|
-
methods!(
|
53
|
-
Xlsx,
|
54
|
-
itself,
|
55
|
-
|
56
|
-
fn ruby_xlsx_new(file: RString) -> AnyObject {
|
57
|
-
let xlsx_reader = Reader::new(file.unwrap().to_string());
|
58
|
-
|
59
|
-
Class::from_existing("Xlsx").wrap_data(xlsx_reader, &*READER_WRAPPER)
|
60
|
-
}
|
61
|
-
|
62
|
-
fn ruby_xlsx_rows() -> AnyObject {
|
63
|
-
itself.get_data(&*READER_WRAPPER).get_rows()
|
64
|
-
}
|
65
|
-
);
|
66
|
-
|
67
|
-
#[no_mangle]
|
68
|
-
#[allow(non_snake_case)]
|
69
|
-
pub extern fn Init_libfastsheet() {
|
70
|
-
Class::new("Xlsx", None).define(|itself| {
|
71
|
-
itself.def_self("new", ruby_xlsx_new);
|
72
|
-
itself.def("rows", ruby_xlsx_rows)
|
73
|
-
});
|
74
|
-
}
|