fastsheet 0.0.7 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
}
|