calx 0.1.0-x86_64-linux
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 +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +145 -0
- data/Rakefile +22 -0
- data/lib/calx/3.3/calx.so +0 -0
- data/lib/calx/3.4/calx.so +0 -0
- data/lib/calx/4.0/calx.so +0 -0
- data/lib/calx/version.rb +6 -0
- data/lib/calx.rb +141 -0
- data/sig/calx.rbs +21 -0
- metadata +62 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 811dfc464f10bee639f21aa1909980c35ea6def130e1d66bed88e26ac191d9f2
|
|
4
|
+
data.tar.gz: e92ae5d0e5e0d6a73b56947627ac0894b3adba9efc591200f9c0a0a293eafe1a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 45242529912a2e7ece7591931ac867c783f9d6ff359931ec56d75101e24b007c65fc6cc2f229064a5b79d32278ce1ed20958c400e483428e1ee77fac40c04116
|
|
7
|
+
data.tar.gz: 799e40aad7093263ec494a5606aa3c5d7732ec6dca35700d310746c71c4e9624f488bd2b464e808c6662cd7a16786a2bffa6f38e74492a19acb7c00720dca245
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zac
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# calx
|
|
2
|
+
|
|
3
|
+
[](https://github.com/7a6163/calx/actions/workflows/main.yml)
|
|
4
|
+
[](https://codecov.io/gh/7a6163/calx)
|
|
5
|
+
|
|
6
|
+
Read CSV, TSV and spreadsheets (xlsx, xlsm, xlsb, xls, ods) row by row from Ruby with a Rust engine ([csv](https://github.com/BurntSushi/rust-csv) and [calamine](https://github.com/tafia/calamine), via [magnus](https://github.com/matsadler/magnus)). The CSV reader is 4–16× faster than Ruby's `CSV`, and the xlsx reader 10–27× faster than simple_xlsx_reader, roo and creek ([benchmarks](#performance)).
|
|
7
|
+
|
|
8
|
+
It only reads. To write xlsx files, see its sibling [fast_xlsx](https://github.com/7a6163/fast_xlsx).
|
|
9
|
+
|
|
10
|
+
> **Status: 0.x.** The API may still change before 1.0.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bundle add calx
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires CRuby 3.3 or later. Installing from source needs a Rust toolchain.
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
require "calx"
|
|
24
|
+
|
|
25
|
+
# One Array per row; the format comes from the file extension.
|
|
26
|
+
Calx.each_row("sales.csv") { |row| p row } # ["North", "120", "9.5"]
|
|
27
|
+
Calx.each_row("sales.xlsx") { |row| p row } # ["North", 120, 9.5, #<Date 2026-07-03>]
|
|
28
|
+
|
|
29
|
+
rows = Calx.read("sales.xlsx", sheet: "July") # every row at once
|
|
30
|
+
Calx.sheet_names("sales.xlsx") # ["June", "July"]
|
|
31
|
+
Calx.each_row("big.csv").first(10) # an Enumerator without a block
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Options:
|
|
35
|
+
|
|
36
|
+
| Option | For | |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `format:` | all | `:csv`, `:tsv`, `:xlsx`, `:xlsm`, `:xlsb`, `:xls` or `:ods`, for files whose extension doesn't tell (uploads saved as temp files) |
|
|
39
|
+
| `sheet:` | spreadsheets | the sheet's index (from 0) or name; the first sheet by default |
|
|
40
|
+
| `col_sep:` | CSV | a one-byte separator; `","` (`"\t"` for `.tsv`) by default |
|
|
41
|
+
| `encoding:` | CSV | the file's encoding, such as `"Big5"` or `"Shift_JIS"`; UTF-8 by default |
|
|
42
|
+
|
|
43
|
+
`path` can be a String or anything with `#to_path` (Pathname, File, Tempfile, Rails' `UploadedFile`).
|
|
44
|
+
|
|
45
|
+
### What you get back
|
|
46
|
+
|
|
47
|
+
- **CSV**: every cell is a String, as with Ruby's `CSV`. A UTF-8 byte order mark is skipped. Rows can have different lengths. A file that isn't valid UTF-8 raises `Calx::Error`, with a hint to pass `encoding:`.
|
|
48
|
+
- **Spreadsheets**: String, Integer, Float, `true`/`false`, `Date`, `Time` or `nil`.
|
|
49
|
+
- Whole numbers come back as Integer (xlsx stores `42` and `42.0` the same way).
|
|
50
|
+
- A date with no time of day is a `Date`; one with a time is a local `Time` (Excel stores no time zone).
|
|
51
|
+
- Durations (`[h]:mm` formats) are the Float number of days Excel stores; in ods files they are ISO 8601 Strings such as `"PT10H10M10S"`.
|
|
52
|
+
- Formulas give the value Excel last saved. Error cells give their text, such as `"#N/A"`.
|
|
53
|
+
- Rows count from the sheet's first row and `row[0]` is always column A, even when the data starts lower: rows above it are `[]` and cells left of it `nil`. Every other row is padded with `nil` to the width of the used range.
|
|
54
|
+
|
|
55
|
+
### Errors
|
|
56
|
+
|
|
57
|
+
`TypeError` for an argument of the wrong type, `ArgumentError` for a value that isn't allowed (an unknown format, sheet name or encoding), `IndexError` for a sheet index past the last sheet, `Errno::ENOENT` for a missing file, and `Calx::Error` for a file that can't be read.
|
|
58
|
+
|
|
59
|
+
### Limits
|
|
60
|
+
|
|
61
|
+
- Read only: no styles, comments or merged-cell ranges, and no editing an existing file.
|
|
62
|
+
- A spreadsheet's sheet is loaded into memory whole before its rows are yielded. A CSV file is read as it goes, except with `encoding:`, which reads the whole file to transcode it.
|
|
63
|
+
|
|
64
|
+
## Performance
|
|
65
|
+
|
|
66
|
+
200,000 rows read with each library, Ruby 3.4.11 with YJIT, Apple Silicon (arm64). Best of 5 runs for CSV, 3 for xlsx; every run must read all 200,000 rows. Lower is better.
|
|
67
|
+
|
|
68
|
+
**CSV, 10 columns of numbers and short strings** (12.5 MB)
|
|
69
|
+
|
|
70
|
+
| Library | Method | Time | vs calx |
|
|
71
|
+
|---|---|---:|---:|
|
|
72
|
+
| **calx** 0.1.0 | `Calx.each_row` | **51 ms** | 1.0× |
|
|
73
|
+
| **calx** 0.1.0 | `Calx.read` | **49 ms** | 1.0× |
|
|
74
|
+
| csv 3.3.7 | `CSV.foreach` | 231 ms | 4.5× slower |
|
|
75
|
+
| csv 3.3.7 | `CSV.read` | 327 ms | 6.4× slower |
|
|
76
|
+
|
|
77
|
+
**CSV, 10 columns with quotes, commas, newlines and CJK** (26.2 MB)
|
|
78
|
+
|
|
79
|
+
| Library | Method | Time | vs calx |
|
|
80
|
+
|---|---|---:|---:|
|
|
81
|
+
| **calx** 0.1.0 | `Calx.each_row` | **64 ms** | 1.0× |
|
|
82
|
+
| **calx** 0.1.0 | `Calx.read` | **64 ms** | 1.0× |
|
|
83
|
+
| csv 3.3.7 | `CSV.foreach` | 865 ms | 13.4× slower |
|
|
84
|
+
| csv 3.3.7 | `CSV.read` | 1,012 ms | 15.7× slower |
|
|
85
|
+
|
|
86
|
+
**xlsx, 5 columns: integer, string, float, date, string** (6.0 MB, written by fast_xlsx)
|
|
87
|
+
|
|
88
|
+
| Library | Method | Time | vs calx |
|
|
89
|
+
|---|---|---:|---:|
|
|
90
|
+
| **calx** 0.1.0 | `Calx.each_row` | **222 ms** | 1.0× |
|
|
91
|
+
| fastsheet 0.2.0 (GitHub) | `Sheet.new(path).rows` | 281 ms | 1.3× slower |
|
|
92
|
+
| simple_xlsx_reader 5.1.0 | `rows.each` | 2,411 ms | 10.8× slower |
|
|
93
|
+
| roo 4.0.0 | `each_row_streaming` | 5,117 ms | 23.0× slower |
|
|
94
|
+
| creek 2.6.3 | `simple_rows` | 6,014 ms | 27.0× slower |
|
|
95
|
+
|
|
96
|
+
Ruby's `CSV` slows down most on quoted fields, which real exports and uploads usually have. The libraries don't return quite the same thing: roo yields cell objects and creek yields a Hash per row, while calx and simple_xlsx_reader yield Arrays of values.
|
|
97
|
+
|
|
98
|
+
fastsheet also uses calamine, so it is close on speed, but it builds every row before returning any and its values differ: whole numbers come back as Float (`1.0`), dates as a `Time` at midnight UTC (so `2026-01-02 08:00 +0800` in Taipei, and the previous day west of UTC), and error cells as `nil`. It reads the first sheet only and no CSV. Its current version is not on RubyGems (0.1.1 there is from 2017), so the benchmark includes it only when it is installed from [GitHub](https://github.com/dmk/fastsheet).
|
|
99
|
+
|
|
100
|
+
Most of calx's time goes into making the Ruby Strings and Arrays: parsing alone is about 5× faster again. Times move by 10–30% between runs, so treat the ratios as rough. Run it yourself:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
bundle exec rake compile
|
|
104
|
+
BUNDLE_GEMFILE=bench/Gemfile bundle install
|
|
105
|
+
BUNDLE_GEMFILE=bench/Gemfile bundle exec ruby --yjit bench/read.rb [rows]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
bin/setup
|
|
112
|
+
bundle exec rake # compile + test + rubocop
|
|
113
|
+
bundle exec rbs -r date -I sig validate
|
|
114
|
+
cargo clippy --manifest-path ext/calx/Cargo.toml --all-targets -- -D warnings
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
All behaviour is tested from Ruby (`test/`); there are no Rust unit tests.
|
|
118
|
+
|
|
119
|
+
Coverage of the Rust extension while the Ruby tests run (needs [cargo-llvm-cov](https://github.com/taiki-e/cargo-llvm-cov) and `rustup component add llvm-tools-preview`). CI uploads it to Codecov.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
rm -rf tmp lib/calx/calx.bundle # force an instrumented rebuild
|
|
123
|
+
eval "$(cargo llvm-cov show-env --sh)"
|
|
124
|
+
bundle exec rake compile test
|
|
125
|
+
cargo llvm-cov report --release # or --lcov / --html
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Rebuild in a clean shell afterwards (`rm -rf tmp && bundle exec rake compile`) so the everyday build is not instrumented.
|
|
129
|
+
|
|
130
|
+
Mutation tests ([cargo-mutants](https://mutants.rs)) change the Rust source one small edit at a time and check that a Ruby test fails for each change. `ext/calx/tests/ruby_suite.rs` connects the two: it rebuilds the extension and runs the Ruby suite, so `cargo test` covers the Ruby tests too. Mutants that can't change behaviour are excluded in `.cargo/mutants.toml`.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
cargo install cargo-mutants
|
|
134
|
+
cargo mutants --file ext/calx/src/lib.rs --jobs 4 # ~1.5 minutes; results in mutants.out/
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Mutants listed in `mutants.out/missed.txt` point at behaviour no test checks. The `Mutation tests` workflow runs this weekly and on demand. Dependabot opens weekly update PRs for the gems, crates and GitHub Actions.
|
|
138
|
+
|
|
139
|
+
The xls, xlsb, ods and some xlsx files in `test/fixtures` come from calamine's test suite (MIT).
|
|
140
|
+
|
|
141
|
+
Releasing: bump `lib/calx/version.rb`, update the CHANGELOG, and push a `vX.Y.Z` tag. The `Build gems` workflow builds the platform gems and publishes them to RubyGems.
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
data/Rakefile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "minitest/test_task"
|
|
5
|
+
|
|
6
|
+
Minitest::TestTask.create
|
|
7
|
+
|
|
8
|
+
require "rubocop/rake_task"
|
|
9
|
+
|
|
10
|
+
RuboCop::RakeTask.new
|
|
11
|
+
|
|
12
|
+
require "rb_sys/extensiontask"
|
|
13
|
+
|
|
14
|
+
task build: :compile
|
|
15
|
+
|
|
16
|
+
GEMSPEC = Gem::Specification.load("calx.gemspec")
|
|
17
|
+
|
|
18
|
+
RbSys::ExtensionTask.new("calx", GEMSPEC) do |ext|
|
|
19
|
+
ext.lib_dir = "lib/calx"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
task default: %i[compile test rubocop]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/calx/version.rb
ADDED
data/lib/calx.rb
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "date"
|
|
4
|
+
require "time"
|
|
5
|
+
require_relative "calx/version"
|
|
6
|
+
|
|
7
|
+
# Reads CSV, TSV and spreadsheets (xlsx, xlsm, xlsb, xls, ods) row by row
|
|
8
|
+
# with a Rust core (the csv and calamine crates).
|
|
9
|
+
#
|
|
10
|
+
# Errors: TypeError for an argument of the wrong type, ArgumentError for a
|
|
11
|
+
# value that isn't allowed, IndexError for a sheet index past the last sheet,
|
|
12
|
+
# Errno::ENOENT for a missing file, and Calx::Error for a file that
|
|
13
|
+
# can't be read.
|
|
14
|
+
module Calx
|
|
15
|
+
# Raised when a file can't be read: corrupt, the wrong format, or (CSV) not
|
|
16
|
+
# in the encoding given.
|
|
17
|
+
class Error < StandardError; end
|
|
18
|
+
|
|
19
|
+
# The native extension looks up Calx::Error, so load it after Error is defined.
|
|
20
|
+
# Precompiled gems ship one binary per Ruby version (calx/3.4/calx);
|
|
21
|
+
# a gem compiled on install has a single calx/calx.
|
|
22
|
+
begin
|
|
23
|
+
require "calx/#{RUBY_VERSION[/\d+\.\d+/]}/calx"
|
|
24
|
+
rescue LoadError
|
|
25
|
+
require "calx/calx"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# File extensions and the format each one is read as.
|
|
29
|
+
FORMATS = {
|
|
30
|
+
".csv" => :csv, ".tsv" => :tsv,
|
|
31
|
+
".xlsx" => :xlsx, ".xlsm" => :xlsm, ".xlsb" => :xlsb, ".xls" => :xls, ".ods" => :ods
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
class << self
|
|
35
|
+
# Yields each row of a sheet as an Array.
|
|
36
|
+
#
|
|
37
|
+
# CSV and TSV cells are Strings. Spreadsheet cells are String, Integer,
|
|
38
|
+
# Float, true/false, Date (a date with no time of day), Time (local time:
|
|
39
|
+
# Excel stores no zone) or nil (empty); error cells are their text, such as
|
|
40
|
+
# "#N/A", and formulas give the value Excel last saved. Row 0 is the
|
|
41
|
+
# sheet's first row and row[0] its column A, even when the data starts lower.
|
|
42
|
+
#
|
|
43
|
+
# @param path [String, #to_path] the file (a Pathname, File or Tempfile works)
|
|
44
|
+
# @param format [Symbol, nil] one of the values of {FORMATS}; by default
|
|
45
|
+
# taken from the file extension
|
|
46
|
+
# @param sheet [Integer, String, nil] spreadsheets only: the sheet's index
|
|
47
|
+
# (from 0) or name; the first sheet by default
|
|
48
|
+
# @param col_sep [String, nil] CSV only: the one-byte separator; "," for
|
|
49
|
+
# CSV and "\t" for TSV by default
|
|
50
|
+
# @param encoding [String, Encoding, nil] CSV only: the file's encoding,
|
|
51
|
+
# such as "Big5" or "Shift_JIS"; UTF-8 by default. A UTF-8 byte order
|
|
52
|
+
# mark is skipped.
|
|
53
|
+
# @yieldparam row [Array]
|
|
54
|
+
# @return [nil, Enumerator] an Enumerator when no block is given
|
|
55
|
+
def each_row(path, format: nil, sheet: nil, col_sep: nil, encoding: nil, &block)
|
|
56
|
+
return enum_for(__method__, path, format:, sheet:, col_sep:, encoding:) unless block
|
|
57
|
+
|
|
58
|
+
path = file_path(path)
|
|
59
|
+
case (format = format_of(path, format))
|
|
60
|
+
when :csv, :tsv
|
|
61
|
+
raise ArgumentError, "sheet: is only for spreadsheets" if sheet
|
|
62
|
+
|
|
63
|
+
each_csv_row(path, format, col_sep, encoding, &block)
|
|
64
|
+
else
|
|
65
|
+
raise ArgumentError, "col_sep: and encoding: are only for CSV files" if col_sep || encoding
|
|
66
|
+
|
|
67
|
+
index, name = sheet_ref(sheet)
|
|
68
|
+
_each_workbook_row(path, format.to_s, index, name, &block)
|
|
69
|
+
end
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Every row of a sheet; takes the options of {each_row}.
|
|
74
|
+
# @return [Array<Array>]
|
|
75
|
+
def read(path, **)
|
|
76
|
+
each_row(path, **).to_a
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# The names of a spreadsheet's sheets, in order.
|
|
80
|
+
# @param path [String, #to_path]
|
|
81
|
+
# @param format [Symbol, nil] as in {each_row}
|
|
82
|
+
# @return [Array<String>]
|
|
83
|
+
def sheet_names(path, format: nil)
|
|
84
|
+
path = file_path(path)
|
|
85
|
+
format = format_of(path, format)
|
|
86
|
+
raise ArgumentError, "a #{format.upcase} file has no sheets" if %i[csv tsv].include?(format)
|
|
87
|
+
|
|
88
|
+
_sheet_names(path, format.to_s)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def file_path(path)
|
|
94
|
+
path = path.to_path if path.respond_to?(:to_path)
|
|
95
|
+
raise TypeError, "path must be a String or respond to #to_path, not #{path.class}" unless path.is_a?(String)
|
|
96
|
+
raise Errno::ENOENT, path unless File.file?(path)
|
|
97
|
+
|
|
98
|
+
path
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def format_of(path, format)
|
|
102
|
+
return FORMATS.fetch(File.extname(path).downcase) { raise ArgumentError, unknown_format(path) } unless format
|
|
103
|
+
unless FORMATS.value?(format)
|
|
104
|
+
raise ArgumentError,
|
|
105
|
+
"unknown format #{format.inspect} (#{FORMATS.values.join(", ")})"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
format
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def unknown_format(path)
|
|
112
|
+
"can't tell the format of #{path.inspect} from its extension; pass format: (#{FORMATS.values.join(", ")})"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def sheet_ref(sheet)
|
|
116
|
+
case sheet
|
|
117
|
+
when nil then [nil, nil]
|
|
118
|
+
when String then [nil, sheet]
|
|
119
|
+
when Integer
|
|
120
|
+
raise IndexError, "sheet index #{sheet} is negative" if sheet.negative?
|
|
121
|
+
|
|
122
|
+
[sheet, nil]
|
|
123
|
+
else raise TypeError, "sheet must be an Integer or String, not #{sheet.class}"
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def each_csv_row(path, format, col_sep, encoding, &)
|
|
128
|
+
col_sep ||= format == :tsv ? "\t" : ","
|
|
129
|
+
raise TypeError, "col_sep must be a String, not #{col_sep.class}" unless col_sep.is_a?(String)
|
|
130
|
+
raise ArgumentError, "col_sep must be one byte, not #{col_sep.inspect}" unless col_sep.bytesize == 1
|
|
131
|
+
|
|
132
|
+
# Transcoded in Ruby, then parsed as UTF-8: the whole file is read into memory.
|
|
133
|
+
if encoding
|
|
134
|
+
encoding = Encoding.find(encoding) # ArgumentError for an unknown name
|
|
135
|
+
bom = "BOM|" if encoding.name.start_with?("UTF-")
|
|
136
|
+
data = File.read(path, encoding: "#{bom}#{encoding}:UTF-8")
|
|
137
|
+
end
|
|
138
|
+
_each_csv_row(path, data, col_sep.ord, &)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
data/sig/calx.rbs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Calx
|
|
2
|
+
VERSION: String
|
|
3
|
+
FORMATS: Hash[String, Symbol]
|
|
4
|
+
|
|
5
|
+
class Error < StandardError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
type path = String | _ToPath
|
|
9
|
+
type cell = String | Integer | Float | bool | Date | Time | nil
|
|
10
|
+
|
|
11
|
+
interface _ToPath
|
|
12
|
+
def to_path: () -> String
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.each_row: (path path, ?format: Symbol?, ?sheet: (Integer | String)?, ?col_sep: String?, ?encoding: (String | Encoding)?) { (Array[cell] row) -> void } -> nil
|
|
16
|
+
| (path path, ?format: Symbol?, ?sheet: (Integer | String)?, ?col_sep: String?, ?encoding: (String | Encoding)?) -> Enumerator[Array[cell], nil]
|
|
17
|
+
|
|
18
|
+
def self.read: (path path, ?format: Symbol?, ?sheet: (Integer | String)?, ?col_sep: String?, ?encoding: (String | Encoding)?) -> Array[Array[cell]]
|
|
19
|
+
|
|
20
|
+
def self.sheet_names: (path path, ?format: Symbol?) -> Array[String]
|
|
21
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: calx
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: x86_64-linux
|
|
6
|
+
authors:
|
|
7
|
+
- Zac
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-11-05 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: Reads CSV, TSV, xlsx, xlsm, xlsb, xls and ods files row by row through
|
|
14
|
+
a native Rust extension (the csv and calamine crates), several times faster than
|
|
15
|
+
Ruby's CSV.
|
|
16
|
+
email:
|
|
17
|
+
- 579103+7a6163@users.noreply.github.com
|
|
18
|
+
executables: []
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE.txt
|
|
24
|
+
- README.md
|
|
25
|
+
- Rakefile
|
|
26
|
+
- lib/calx.rb
|
|
27
|
+
- lib/calx/3.3/calx.so
|
|
28
|
+
- lib/calx/3.4/calx.so
|
|
29
|
+
- lib/calx/4.0/calx.so
|
|
30
|
+
- lib/calx/version.rb
|
|
31
|
+
- sig/calx.rbs
|
|
32
|
+
homepage: https://github.com/7a6163/calx
|
|
33
|
+
licenses:
|
|
34
|
+
- MIT
|
|
35
|
+
metadata:
|
|
36
|
+
homepage_uri: https://github.com/7a6163/calx
|
|
37
|
+
source_code_uri: https://github.com/7a6163/calx
|
|
38
|
+
changelog_uri: https://github.com/7a6163/calx/blob/main/CHANGELOG.md
|
|
39
|
+
rubygems_mfa_required: 'true'
|
|
40
|
+
post_install_message:
|
|
41
|
+
rdoc_options: []
|
|
42
|
+
require_paths:
|
|
43
|
+
- lib
|
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '3.3'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: 4.1.dev
|
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
53
|
+
requirements:
|
|
54
|
+
- - ">="
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '0'
|
|
57
|
+
requirements: []
|
|
58
|
+
rubygems_version: 3.5.23
|
|
59
|
+
signing_key:
|
|
60
|
+
specification_version: 4
|
|
61
|
+
summary: Fast CSV and spreadsheet reader for Ruby with a Rust engine
|
|
62
|
+
test_files: []
|