hypertabular-wasm 0.7.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +179 -0
- data/ext/hypertabular_native/3.4/hypertabular_native.a.gz +0 -0
- data/ext/hypertabular_native/4.0/hypertabular_native.a.gz +0 -0
- data/ext/hypertabular_native/extconf.rb +108 -0
- data/lib/hypertabular/batch.rb +204 -0
- data/lib/hypertabular/column.rb +170 -0
- data/lib/hypertabular/delimited_reader.rb +309 -0
- data/lib/hypertabular/dialect.rb +42 -0
- data/lib/hypertabular/runtime/columns.rb +46 -0
- data/lib/hypertabular/runtime/delimited.rb +204 -0
- data/lib/hypertabular/runtime/workbook.rb +299 -0
- data/lib/hypertabular/runtime.rb +142 -0
- data/lib/hypertabular/tabular_error.rb +99 -0
- data/lib/hypertabular/workbook.rb +173 -0
- data/lib/hypertabular.rb +103 -0
- metadata +107 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module HyperTabular
|
|
2
|
+
# A structural failure: the input is not rows of cells — a record of the wrong width,
|
|
3
|
+
# input that ends inside a quoted cell, a workbook whose container or parts cannot be
|
|
4
|
+
# read. Never a cell's verdict: a value that does not cast is a HyperCast::Fault in its
|
|
5
|
+
# column, and the read goes on. A structural failure ends the input, after every intact
|
|
6
|
+
# row before it has been delivered.
|
|
7
|
+
#
|
|
8
|
+
# For a workbook, #record is the part the failure is in, #line the sheet row and #byte the
|
|
9
|
+
# offset within the part's inflated bytes.
|
|
10
|
+
#
|
|
11
|
+
# begin
|
|
12
|
+
# reader.read
|
|
13
|
+
# rescue HyperTabular::TabularError => e
|
|
14
|
+
# case e
|
|
15
|
+
# in { kind: :column_count, line:, expected:, found: }
|
|
16
|
+
# warn "line #{line}: #{found} cells, not #{expected}"
|
|
17
|
+
# in { kind: :unclosed_quote, line: }
|
|
18
|
+
# warn "line #{line}: a quote is never closed"
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
class TabularError < StandardError
|
|
22
|
+
# What the core's failure codes mean.
|
|
23
|
+
KINDS = {
|
|
24
|
+
1 => :unclosed_quote, 2 => :column_count, 3 => :row_too_long, 16 => :not_a_zip, 17 => :container,
|
|
25
|
+
18 => :encrypted, 19 => :method, 20 => :missing_part, 21 => :xml, 22 => :deflate,
|
|
26
|
+
23 => :not_a_workbook, 24 => :shared_string, 25 => :too_large
|
|
27
|
+
}.freeze
|
|
28
|
+
|
|
29
|
+
# What is wrong: :unclosed_quote (the input ended inside a quoted cell), :column_count
|
|
30
|
+
# (a record's cell count disagrees with the first record's), :row_too_long (a single
|
|
31
|
+
# record is larger than DelimitedReader::MAX_ROW_BYTES); for a workbook :not_a_zip,
|
|
32
|
+
# :container (the zip's own structure is broken), :encrypted, :method (a part is
|
|
33
|
+
# compressed by neither stored nor deflate), :missing_part, :xml (a part ends inside an
|
|
34
|
+
# XML construct), :deflate (a part is not a whole deflate stream), :not_a_workbook,
|
|
35
|
+
# :shared_string (a cell names a shared string the table does not have) or :too_large.
|
|
36
|
+
attr_reader :kind
|
|
37
|
+
|
|
38
|
+
# Zero-based index of the offending record — the header and skipped blank lines
|
|
39
|
+
# included.
|
|
40
|
+
attr_reader :record
|
|
41
|
+
|
|
42
|
+
# One-based line the offending record starts on.
|
|
43
|
+
attr_reader :line
|
|
44
|
+
|
|
45
|
+
# Absolute byte offset of the offending record's start, a byte-order mark included.
|
|
46
|
+
attr_reader :byte
|
|
47
|
+
|
|
48
|
+
# Cells in the first record, for :column_count; 0 otherwise.
|
|
49
|
+
attr_reader :expected
|
|
50
|
+
|
|
51
|
+
# Cells in this record, for :column_count; 0 otherwise.
|
|
52
|
+
attr_reader :found
|
|
53
|
+
|
|
54
|
+
# The failure the core reported, as [code, line, record, byte, expected, found]. A code
|
|
55
|
+
# this binding does not know is the container's: the most general refusal.
|
|
56
|
+
def self.from(failure)
|
|
57
|
+
code, line, record, byte, expected, found = failure
|
|
58
|
+
new(KINDS.fetch(code, :container), record, line, byte, expected, found)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Raised by the readers; nothing else builds one.
|
|
62
|
+
def initialize(kind, record, line, byte, expected = 0, found = 0)
|
|
63
|
+
@kind = kind
|
|
64
|
+
@record = record
|
|
65
|
+
@line = line
|
|
66
|
+
@byte = byte
|
|
67
|
+
@expected = expected
|
|
68
|
+
@found = found
|
|
69
|
+
super(describe)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# The failure as a Hash, which is what lets `case error in { kind:, line: }` match.
|
|
73
|
+
def deconstruct_keys(_keys)
|
|
74
|
+
{ kind: kind, record: record, line: line, byte: byte, expected: expected, found: found }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
# The message, in the words the other bindings use.
|
|
80
|
+
def describe
|
|
81
|
+
at = "#{record} (line #{line}, byte #{byte})"
|
|
82
|
+
case kind
|
|
83
|
+
when :column_count then "Record #{at} has #{found} cells; the first record had #{expected}."
|
|
84
|
+
when :unclosed_quote then "The input ended inside a quoted cell in record #{at}."
|
|
85
|
+
when :row_too_long then "Record #{at} exceeds the #{DelimitedReader::MAX_ROW_BYTES}-byte row ceiling."
|
|
86
|
+
when :not_a_zip then "The workbook is not a zip file."
|
|
87
|
+
when :encrypted then "The workbook is encrypted."
|
|
88
|
+
when :method then "Part #{record} of the workbook is compressed by method #{found}, neither stored nor deflate."
|
|
89
|
+
when :missing_part then "Part #{record}, which the workbook cannot be read without, is missing."
|
|
90
|
+
when :xml then "Part #{record} of the workbook ends inside an XML construct (byte #{byte})."
|
|
91
|
+
when :deflate then "Part #{record} of the workbook is not a whole deflate stream (byte #{byte})."
|
|
92
|
+
when :not_a_workbook then "The zip is neither an XLSX nor an ODS workbook."
|
|
93
|
+
when :shared_string then "Row #{line} names shared string #{found}; the table has #{expected}."
|
|
94
|
+
when :too_large then "The workbook holds more text than a batch can address."
|
|
95
|
+
else "The workbook's zip structure is broken."
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
module HyperTabular
|
|
2
|
+
# One sheet of a workbook, as Workbook#sheets lists it: its name, and whether the workbook
|
|
3
|
+
# hides it. A hidden sheet reads like any other.
|
|
4
|
+
SheetInfo = Data.define(:name, :hidden)
|
|
5
|
+
|
|
6
|
+
# How a sheet is read: whether its first row is a header (exposed through Sheet#header
|
|
7
|
+
# and never delivered as a row), whether a row with no cells is skipped, and the most rows
|
|
8
|
+
# a batch holds.
|
|
9
|
+
SheetOptions = Data.define(:has_header, :skip_empty_rows, :batch_rows) do
|
|
10
|
+
# Every option has a default; ArgumentError unless +batch_rows+ is a positive Integer.
|
|
11
|
+
def initialize(has_header: true, skip_empty_rows: true, batch_rows: DelimitedReader::DEFAULT_BATCH_ROWS)
|
|
12
|
+
raise ArgumentError, "batch_rows must be a positive Integer; got #{batch_rows.inspect}" unless
|
|
13
|
+
batch_rows.is_a?(Integer) && batch_rows.positive?
|
|
14
|
+
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# A header, empty rows skipped, DelimitedReader::DEFAULT_BATCH_ROWS rows a batch.
|
|
20
|
+
SheetOptions::DEFAULT = SheetOptions.new
|
|
21
|
+
|
|
22
|
+
# An XLSX or ODS workbook held in memory, its sheets listed and its shared strings and
|
|
23
|
+
# styles loaded: what a Sheet reads from.
|
|
24
|
+
#
|
|
25
|
+
# book = HyperTabular::Workbook.open("orders.xlsx")
|
|
26
|
+
# book.sheets # => [#<data HyperTabular::SheetInfo name="Orders", hidden=false>]
|
|
27
|
+
# sheet = book.sheet("Orders", HyperTabular::SheetOptions::DEFAULT, plan)
|
|
28
|
+
# while (batch = sheet.read) ... end
|
|
29
|
+
#
|
|
30
|
+
# A workbook that cannot be read — not a zip, encrypted, a part missing or broken — is a
|
|
31
|
+
# TabularError from Workbook.new. Its sheets may be read at once, each with buffers of its
|
|
32
|
+
# own. Not thread-safe.
|
|
33
|
+
class Workbook
|
|
34
|
+
# Reads the file at +path+ into memory and opens it.
|
|
35
|
+
def self.open(path)
|
|
36
|
+
new(File.binread(path))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Opens the workbook in +bytes+, a String, which is read in place: a frozen String is
|
|
40
|
+
# the workbook's as it is, anything else is copied once.
|
|
41
|
+
def initialize(bytes)
|
|
42
|
+
raise ArgumentError, "bytes must be a String; got #{bytes.class}" unless bytes.is_a?(String)
|
|
43
|
+
|
|
44
|
+
bytes = bytes.b.freeze unless bytes.frozen?
|
|
45
|
+
@book = Runtime::Book::Opened.new(bytes)
|
|
46
|
+
@sheets = @book.sheets.map { |name, hidden, _part, _index| SheetInfo.new(name: name, hidden: hidden) }.freeze
|
|
47
|
+
rescue Runtime::Book::StructureError => e
|
|
48
|
+
raise TabularError.from(e.failure)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The workbook's sheets, in its own order, as SheetInfo. Sheets that hold no cells
|
|
52
|
+
# (chart sheets, macro sheets) are not among them.
|
|
53
|
+
attr_reader :sheets
|
|
54
|
+
|
|
55
|
+
# Which kind of workbook this is: :xlsx or :ods.
|
|
56
|
+
def format
|
|
57
|
+
@book.format
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# The date system the workbook's serials count in — what a date-formatted number is
|
|
61
|
+
# read by: :y1900 or :y1904, as HyperCast names them.
|
|
62
|
+
def date_system
|
|
63
|
+
HyperCast::EXCEL_EPOCHS.key(@book.epoch)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Starts a read of one sheet — +which+ is its index in #sheets or its name — through
|
|
67
|
+
# +plan+, as +options+ (SheetOptions) says. With a header declared the header row is
|
|
68
|
+
# read here. IndexError for an index the workbook has no sheet at, KeyError for a name
|
|
69
|
+
# it has no sheet by.
|
|
70
|
+
def sheet(which, options, plan)
|
|
71
|
+
index =
|
|
72
|
+
case which
|
|
73
|
+
when Integer
|
|
74
|
+
raise IndexError, "the workbook has #{@sheets.size} sheets, and no sheet #{which}" unless
|
|
75
|
+
which >= 0 && which < @sheets.size
|
|
76
|
+
|
|
77
|
+
which
|
|
78
|
+
when String
|
|
79
|
+
found = @sheets.index { |sheet| sheet.name == which }
|
|
80
|
+
raise KeyError, "the workbook has no sheet named #{which.inspect}" if found.nil?
|
|
81
|
+
|
|
82
|
+
found
|
|
83
|
+
else raise ArgumentError, "which must be an Integer or a String; got #{which.inspect}"
|
|
84
|
+
end
|
|
85
|
+
Sheet.new(@book, @book.sheets[index], options, plan)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# The workbook in a line.
|
|
89
|
+
def inspect
|
|
90
|
+
"#<#{self.class.name} #{format} sheets=#{@sheets.size}>"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# A forward-only read of one sheet of a Workbook, a batch at a time, through a plan — into
|
|
95
|
+
# the same Batch delimited text is read into.
|
|
96
|
+
#
|
|
97
|
+
# A typed cell is converted directly by its door — a stored 42.0 never passes through text
|
|
98
|
+
# to become an Integer — and a text cell goes through the door as delimited text would. A
|
|
99
|
+
# sheet that is structurally broken raises a TabularError after every intact row before
|
|
100
|
+
# the break has been delivered, and the same error again on every later read.
|
|
101
|
+
class Sheet
|
|
102
|
+
# The SheetOptions the sheet is read with.
|
|
103
|
+
attr_reader :options
|
|
104
|
+
|
|
105
|
+
# The plan: the output Columns, in output order.
|
|
106
|
+
attr_reader :plan
|
|
107
|
+
|
|
108
|
+
# The header row's names — a typed cell said the way the text door says it — as frozen
|
|
109
|
+
# UTF-8 Strings, or nil when the options declare no header.
|
|
110
|
+
attr_reader :header
|
|
111
|
+
|
|
112
|
+
# Made by Workbook#sheet.
|
|
113
|
+
def initialize(book, sheet, options, plan)
|
|
114
|
+
raise ArgumentError, "options must be a HyperTabular::SheetOptions; got #{options.inspect}" unless
|
|
115
|
+
options.is_a?(SheetOptions)
|
|
116
|
+
|
|
117
|
+
@plan = Array(plan).dup.freeze
|
|
118
|
+
@plan.each_with_index do |column, index|
|
|
119
|
+
raise ArgumentError, "plan column #{index} must be a HyperTabular::Column; got #{column.inspect}" unless
|
|
120
|
+
column.is_a?(Column)
|
|
121
|
+
end
|
|
122
|
+
@book = book
|
|
123
|
+
@options = options
|
|
124
|
+
width = (@plan.map(&:ordinal).max || -1) + 1
|
|
125
|
+
@reading = Runtime::Book::Reading.new(book, sheet, options.has_header, options.skip_empty_rows,
|
|
126
|
+
@plan.map(&:packed), @plan.map(&:value_bytes), options.batch_rows, width)
|
|
127
|
+
@header = @reading.header
|
|
128
|
+
@pending = nil
|
|
129
|
+
@failure = nil
|
|
130
|
+
rescue Runtime::Book::StructureError => e
|
|
131
|
+
raise TabularError.from(e.failure)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Reads the next batch: a Batch of up to the options' batch_rows rows, or nil once the
|
|
135
|
+
# sheet has no more.
|
|
136
|
+
def read
|
|
137
|
+
if @pending
|
|
138
|
+
@failure = @pending
|
|
139
|
+
@pending = nil
|
|
140
|
+
end
|
|
141
|
+
raise @failure if @failure
|
|
142
|
+
|
|
143
|
+
rows, cells, arena, failure = @reading.fill
|
|
144
|
+
if failure
|
|
145
|
+
broken = TabularError.from(failure)
|
|
146
|
+
raise @failure = broken if rows.zero?
|
|
147
|
+
|
|
148
|
+
@pending = broken
|
|
149
|
+
end
|
|
150
|
+
return nil if rows.zero?
|
|
151
|
+
|
|
152
|
+
columns = @reading.columns
|
|
153
|
+
values = Array.new(@plan.size) { |column| columns.values(column, rows) }
|
|
154
|
+
Batch.new(@plan, rows, values, Array.new(@plan.size) { |column| columns.verdicts(column, rows) },
|
|
155
|
+
cells, @reading.per_row, @book.strings, 0, arena, workbook: true)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Every remaining batch. Without a block, an Enumerator.
|
|
159
|
+
def each_batch
|
|
160
|
+
return to_enum(:each_batch) unless block_given?
|
|
161
|
+
|
|
162
|
+
while (batch = read)
|
|
163
|
+
yield batch
|
|
164
|
+
end
|
|
165
|
+
self
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# The sheet in a line.
|
|
169
|
+
def inspect
|
|
170
|
+
"#<#{self.class.name} columns=#{@plan.size}>"
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
data/lib/hypertabular.rb
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require "date"
|
|
2
|
+
require "hypercast"
|
|
3
|
+
require_relative "hypertabular/runtime"
|
|
4
|
+
require_relative "hypertabular/runtime/columns"
|
|
5
|
+
require_relative "hypertabular/runtime/delimited"
|
|
6
|
+
require_relative "hypertabular/runtime/workbook"
|
|
7
|
+
|
|
8
|
+
# Tabular parsing with a HyperCast verdict for every cell: delimited text — CSV, TSV, any
|
|
9
|
+
# single-byte ASCII separator — and workbooks — XLSX and ODS — read a batch at a time into
|
|
10
|
+
# typed columns by one Rust core (libhypertabular), called through Fiddle.
|
|
11
|
+
#
|
|
12
|
+
# plan = [HyperTabular::Column.i32(0), HyperTabular::Column.text(1)]
|
|
13
|
+
# reader = HyperTabular::DelimitedReader.new("id,name\n1,alice\nx,bob\n", HyperTabular::Dialect::CSV, plan)
|
|
14
|
+
# reader.header # => ["id", "name"]
|
|
15
|
+
# batch = reader.read # => #<HyperTabular::Batch rows=2 columns=2>
|
|
16
|
+
# batch.values(1) # => ["alice", "bob"]
|
|
17
|
+
# batch.get(0, 1) # => #<data HyperCast::Fault reason=:malformed, offset=0, length=1>
|
|
18
|
+
# batch.raw(0, 1) # => "x"
|
|
19
|
+
# batch.line(1) # => 3
|
|
20
|
+
#
|
|
21
|
+
# book = HyperTabular::Workbook.open("orders.xlsx")
|
|
22
|
+
# sheet = book.sheet("Orders", HyperTabular::SheetOptions::DEFAULT, plan)
|
|
23
|
+
# sheet.read # => the same Batch
|
|
24
|
+
#
|
|
25
|
+
# Nothing is sniffed: the Dialect states the separator, the quoting and the header,
|
|
26
|
+
# SheetOptions a sheet's header and whether empty rows are skipped, and the plan states each
|
|
27
|
+
# Column's door and, for numbers, its notation. HyperCast is the judge:
|
|
28
|
+
# the verdicts (HyperCast::Success, HyperCast::Fault), the notation (HyperCast::NumFormat),
|
|
29
|
+
# the exact decimal (HyperCast::Decimal) and the declared options are that gem's own types,
|
|
30
|
+
# and a cell means exactly what HyperCast's door would say of the same text. A bad value is
|
|
31
|
+
# a verdict; a broken file is a TabularError.
|
|
32
|
+
module HyperTabular
|
|
33
|
+
# This gem's own version — kept in lockstep with hypertabular.gemspec and the core's
|
|
34
|
+
# rust/Cargo.toml.
|
|
35
|
+
VERSION = "0.7.0"
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
# Whether the native core loaded and exports the ABI this binding was built against.
|
|
39
|
+
# Probed once (a native_version round trip: the cheapest call the core has), cached,
|
|
40
|
+
# and never raises: a missing shared library and an unsupported platform both answer
|
|
41
|
+
# false. A reader keeps its own behavior — building one without the core raises the
|
|
42
|
+
# precise LoadError — this only answers the question quietly.
|
|
43
|
+
def available?
|
|
44
|
+
return @available unless @available.nil?
|
|
45
|
+
|
|
46
|
+
@available = begin
|
|
47
|
+
native_version.is_a?(String)
|
|
48
|
+
rescue LoadError, StandardError
|
|
49
|
+
false
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The version of the native core actually loaded, as "major.minor.patch" — read from
|
|
54
|
+
# the library itself, not from this gem, so a consumer can prove the core behind the
|
|
55
|
+
# reader is the one this binding was built against.
|
|
56
|
+
def native_version
|
|
57
|
+
HyperCast::Interop.version(Runtime::Delimited.version)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
require_relative "hypertabular/dialect"
|
|
63
|
+
require_relative "hypertabular/column"
|
|
64
|
+
require_relative "hypertabular/tabular_error"
|
|
65
|
+
require_relative "hypertabular/batch"
|
|
66
|
+
require_relative "hypertabular/delimited_reader"
|
|
67
|
+
require_relative "hypertabular/workbook"
|
|
68
|
+
|
|
69
|
+
# --- backend selection, as hypercast makes its own: the Magnus extension, when present,
|
|
70
|
+
# replaces the Fiddle crossing in place. Every native call and every byte of native memory is
|
|
71
|
+
# behind HyperTabular::Runtime (Runtime::Delimited, Runtime::Book::Opened and ::Reading, and
|
|
72
|
+
# Runtime.unescape), so that is all the extension redefines (rust/src/ruby_ext.rs): the
|
|
73
|
+
# constructors hand back objects of its own that answer the same methods with the same bytes,
|
|
74
|
+
# and everything above them — the readers, the workbook, Batch and every value and verdict it
|
|
75
|
+
# builds — is the same Ruby on either backend. It is how the precompiled platform gems ship,
|
|
76
|
+
# carrying no Fiddle library at all, and how the hypertabular-wasm gem runs in ruby.wasm,
|
|
77
|
+
# where there is no Fiddle to call. The Fiddle definitions stay the universal zero-compile
|
|
78
|
+
# fallback, for a Ruby or a platform no platform gem covers.
|
|
79
|
+
#
|
|
80
|
+
# HYPERTABULAR_PURE forces Fiddle, as HYPERCAST_PURE does for hypercast. It is a testing and
|
|
81
|
+
# diagnostic switch — CI runs the whole suite through it — read for presence, not value.
|
|
82
|
+
# Inside a platform gem there is no library for it to load: BACKEND still reads :fiddle,
|
|
83
|
+
# HyperTabular.available? answers false, and the first reader raises the LoadError naming the
|
|
84
|
+
# missing library.
|
|
85
|
+
HyperTabular::BACKEND =
|
|
86
|
+
if ENV["HYPERTABULAR_PURE"]
|
|
87
|
+
:fiddle
|
|
88
|
+
else
|
|
89
|
+
# A platform gem carries one extension per Ruby ABI under lib/hypertabular/<minor>/, and
|
|
90
|
+
# `rake native:dev` stages its own build there too; CI's in-job staging drops a single
|
|
91
|
+
# one flat at lib/. A miss on both is not an error: it is what the Fiddle backend is for.
|
|
92
|
+
begin
|
|
93
|
+
require "hypertabular/#{RUBY_VERSION[/\d+\.\d+/]}/hypertabular_native"
|
|
94
|
+
:native
|
|
95
|
+
rescue LoadError
|
|
96
|
+
begin
|
|
97
|
+
require "hypertabular_native"
|
|
98
|
+
:native
|
|
99
|
+
rescue LoadError
|
|
100
|
+
:fiddle
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hypertabular-wasm
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.7.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Brian Buvinghausen
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: hypercast-wasm
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.7.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.7.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: yard
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.9'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.9'
|
|
54
|
+
description: |
|
|
55
|
+
The hypertabular gem's Magnus extension, prebuilt for wasm32-wasip1, for a ruby.wasm
|
|
56
|
+
interpreter built with `rbwasm build`: put this gem in that Gemfile instead of
|
|
57
|
+
hypertabular and the extension is linked into the interpreter, with hypercast-wasm beside
|
|
58
|
+
it. Same API and the same Rust core as the hypertabular gem. Not for a host Ruby, where
|
|
59
|
+
the hypertabular gem is the one to install.
|
|
60
|
+
executables: []
|
|
61
|
+
extensions:
|
|
62
|
+
- ext/hypertabular_native/extconf.rb
|
|
63
|
+
extra_rdoc_files: []
|
|
64
|
+
files:
|
|
65
|
+
- LICENSE
|
|
66
|
+
- README.md
|
|
67
|
+
- ext/hypertabular_native/3.4/hypertabular_native.a.gz
|
|
68
|
+
- ext/hypertabular_native/4.0/hypertabular_native.a.gz
|
|
69
|
+
- ext/hypertabular_native/extconf.rb
|
|
70
|
+
- lib/hypertabular.rb
|
|
71
|
+
- lib/hypertabular/batch.rb
|
|
72
|
+
- lib/hypertabular/column.rb
|
|
73
|
+
- lib/hypertabular/delimited_reader.rb
|
|
74
|
+
- lib/hypertabular/dialect.rb
|
|
75
|
+
- lib/hypertabular/runtime.rb
|
|
76
|
+
- lib/hypertabular/runtime/columns.rb
|
|
77
|
+
- lib/hypertabular/runtime/delimited.rb
|
|
78
|
+
- lib/hypertabular/runtime/workbook.rb
|
|
79
|
+
- lib/hypertabular/tabular_error.rb
|
|
80
|
+
- lib/hypertabular/workbook.rb
|
|
81
|
+
homepage: https://github.com/SkunkWerkx/HyperTabular
|
|
82
|
+
licenses:
|
|
83
|
+
- MIT
|
|
84
|
+
metadata:
|
|
85
|
+
source_code_uri: https://github.com/SkunkWerkx/HyperTabular
|
|
86
|
+
bug_tracker_uri: https://github.com/SkunkWerkx/HyperTabular/issues
|
|
87
|
+
changelog_uri: https://github.com/SkunkWerkx/HyperTabular/blob/master/CHANGELOG.md
|
|
88
|
+
documentation_uri: https://github.com/SkunkWerkx/HyperTabular/tree/master/ruby#ruby-in-the-browser
|
|
89
|
+
rubygems_mfa_required: 'true'
|
|
90
|
+
rdoc_options: []
|
|
91
|
+
require_paths:
|
|
92
|
+
- lib
|
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
|
+
requirements:
|
|
95
|
+
- - ">="
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '3.3'
|
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
requirements: []
|
|
104
|
+
rubygems_version: 4.0.20
|
|
105
|
+
specification_version: 4
|
|
106
|
+
summary: 'HyperTabular for ruby.wasm: the Magnus extension, linked in by rbwasm build'
|
|
107
|
+
test_files: []
|