xlsxrb 0.1.3 → 0.1.4
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/.devcontainer/Dockerfile +1 -0
- data/Rakefile +103 -88
- data/benchmark.rb +1 -0
- data/lib/xlsxrb/version.rb +1 -1
- metadata +1 -29
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 87ffb5143d51491cc1a7389a3aa673c40cc02e4e5ee69c554b795754f707c3cd
|
|
4
|
+
data.tar.gz: 1581c2a99238745d5e6cf65700cd3defbe8b3a3afadb55e74a44b647294407b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2d994013d341f16ab98f33a190f58fd401247813a9451254eaa46809610e45a2840dc13d9e66b2b5d8c8edf0af6207eab38c9a3d0a2184fa781ff8a80dc63ed2
|
|
7
|
+
data.tar.gz: 9e92cd503f6a64dcdcf804996e85af09e2a11b4ceebb4586a6d0a688af81ec4cf66f7be0c86fced3acc90f76ca60dd26123aa1440b49740b1bc9522399877974
|
data/.devcontainer/Dockerfile
CHANGED
data/Rakefile
CHANGED
|
@@ -192,11 +192,13 @@ task :wasm do
|
|
|
192
192
|
require "open-uri"
|
|
193
193
|
wasm_url = "https://cdn.jsdelivr.net/npm/@ruby/4.0-wasm-wasi@2.9.3-2.9.4/dist/ruby.wasm"
|
|
194
194
|
FileUtils.mkdir_p(File.dirname(original_wasm_cache))
|
|
195
|
+
# rubocop:disable Security/Open
|
|
195
196
|
URI.open(wasm_url) do |stream|
|
|
196
197
|
File.open(original_wasm_cache, "wb") do |file|
|
|
197
198
|
IO.copy_stream(stream, file)
|
|
198
199
|
end
|
|
199
200
|
end
|
|
201
|
+
# rubocop:enable Security/Open
|
|
200
202
|
puts "Original ruby.wasm cached successfully."
|
|
201
203
|
end
|
|
202
204
|
|
|
@@ -231,9 +233,7 @@ task :wasm do
|
|
|
231
233
|
|
|
232
234
|
# Resolve and copy host's rexml files
|
|
233
235
|
rexml_spec_path = $LOAD_PATH.find { |p| File.exist?(File.join(p, "rexml/rexml.rb")) }
|
|
234
|
-
if rexml_spec_path
|
|
235
|
-
FileUtils.cp_r(File.join(rexml_spec_path, "rexml"), bundle_assets_dir)
|
|
236
|
-
end
|
|
236
|
+
FileUtils.cp_r(File.join(rexml_spec_path, "rexml"), bundle_assets_dir) if rexml_spec_path
|
|
237
237
|
|
|
238
238
|
# Gateway for strscan
|
|
239
239
|
File.write(File.join(bundle_assets_dir, "strscan.rb"), <<~RUBY)
|
|
@@ -259,11 +259,11 @@ task :wasm do
|
|
|
259
259
|
]
|
|
260
260
|
stdlib_files.each do |name|
|
|
261
261
|
path = $LOAD_PATH.find { |p| File.exist?(File.join(p, name)) }
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
262
|
+
next unless path
|
|
263
|
+
|
|
264
|
+
dest_path = File.join(bundle_assets_dir, name)
|
|
265
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
|
266
|
+
FileUtils.cp(File.join(path, name), dest_path)
|
|
267
267
|
end
|
|
268
268
|
|
|
269
269
|
# C. Patch tmpdir.rb to automatically create /tmp in Wasm virtual filesystem (since Wasm has no writable /tmp by default)
|
|
@@ -286,92 +286,104 @@ task :wasm do
|
|
|
286
286
|
puts "Building packed ruby.wasm from staging bundle..."
|
|
287
287
|
cmd = "bundle exec rbwasm pack #{original_wasm_cache} --dir #{wasm_bundle_dir}::/usr/local/lib/ruby/site_ruby -o #{packed_wasm_path}"
|
|
288
288
|
puts "Executing: #{cmd}"
|
|
289
|
-
unless system(cmd)
|
|
290
|
-
raise "Failed to build packed ruby.wasm using rbwasm pack!"
|
|
291
|
-
end
|
|
289
|
+
raise "Failed to build packed ruby.wasm using rbwasm pack!" unless system(cmd)
|
|
292
290
|
end
|
|
293
291
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
require 'fileutils'
|
|
292
|
+
def download_file(url, dest)
|
|
293
|
+
# Check if the file exists and is not a tiny placeholder/error document
|
|
294
|
+
return if File.exist?(dest) && File.size(dest) > 1024
|
|
298
295
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return if File.exist?(dest) && File.size(dest) > 1024
|
|
296
|
+
puts "Downloading #{url} to #{dest}..."
|
|
297
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
302
298
|
|
|
303
|
-
|
|
304
|
-
|
|
299
|
+
require "net/http"
|
|
300
|
+
uri = URI.parse(url)
|
|
301
|
+
temp_dest = "#{dest}.tmp"
|
|
305
302
|
|
|
306
|
-
|
|
307
|
-
uri
|
|
308
|
-
|
|
303
|
+
begin
|
|
304
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https", open_timeout: 15, read_timeout: 90) do |http|
|
|
305
|
+
request = Net::HTTP::Get.new(uri)
|
|
306
|
+
# Specify User-Agent to bypass scraping prevention on CDNs and act as a normal browser
|
|
307
|
+
request["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
308
|
+
# Force raw (identity) encoding to prevent receiving Brotli-compressed (.br) data,
|
|
309
|
+
# which Ruby's Net::HTTP cannot decode automatically, leading to corrupted Wasm files.
|
|
310
|
+
request["Accept-Encoding"] = "identity"
|
|
309
311
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
request = Net::HTTP::Get.new(uri)
|
|
313
|
-
# Specify User-Agent to bypass scraping prevention on CDNs and act as a normal browser
|
|
314
|
-
request['User-Agent'] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
|
315
|
-
# Force raw (identity) encoding to prevent receiving Brotli-compressed (.br) data,
|
|
316
|
-
# which Ruby's Net::HTTP cannot decode automatically, leading to corrupted Wasm files.
|
|
317
|
-
request['Accept-Encoding'] = 'identity'
|
|
318
|
-
|
|
319
|
-
http.request(request) do |response|
|
|
320
|
-
if response.code.to_i != 200
|
|
321
|
-
raise "HTTP error #{response.code}: #{response.message}"
|
|
322
|
-
end
|
|
312
|
+
http.request(request) do |response|
|
|
313
|
+
raise "HTTP error #{response.code}: #{response.message}" if response.code.to_i != 200
|
|
323
314
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
end
|
|
315
|
+
File.open(temp_dest, "wb") do |output|
|
|
316
|
+
response.read_body do |chunk|
|
|
317
|
+
output.write(chunk)
|
|
328
318
|
end
|
|
329
319
|
end
|
|
330
320
|
end
|
|
331
|
-
# Atomic rename to prevent leaving incomplete files on failure
|
|
332
|
-
File.rename(temp_dest, dest)
|
|
333
|
-
puts "Downloaded successfully."
|
|
334
|
-
rescue => e
|
|
335
|
-
puts "Failed to download #{url}: #{e.message}"
|
|
336
|
-
File.delete(temp_dest) if File.exist?(temp_dest)
|
|
337
|
-
File.delete(dest) if File.exist?(dest)
|
|
338
|
-
raise "Required asset download failed. Build aborted."
|
|
339
321
|
end
|
|
322
|
+
# Atomic rename to prevent leaving incomplete files on failure
|
|
323
|
+
File.rename(temp_dest, dest)
|
|
324
|
+
puts "Downloaded successfully."
|
|
325
|
+
|
|
326
|
+
# Dynamic Brotli Decompression if the server ignored identity encoding and sent Brotli (.br) data
|
|
327
|
+
if File.exist?(dest) && File.binread(dest, 4)&.bytes == [0xCF, 0xFF, 0xFF, 0x7F]
|
|
328
|
+
puts "Detected Brotli compression on #{dest}. Decompressing..."
|
|
329
|
+
|
|
330
|
+
unpacked = "#{dest}.unpacked"
|
|
331
|
+
if system("brotli -d -f -o #{unpacked} #{dest}")
|
|
332
|
+
File.rename(unpacked, dest)
|
|
333
|
+
puts "Decompressed #{dest} successfully."
|
|
334
|
+
else
|
|
335
|
+
FileUtils.rm_f(unpacked)
|
|
336
|
+
raise "Failed to decompress Brotli file: #{dest}"
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
rescue StandardError => e
|
|
340
|
+
puts "Failed to download #{url}: #{e.message}"
|
|
341
|
+
FileUtils.rm_f(temp_dest)
|
|
342
|
+
FileUtils.rm_f(dest)
|
|
343
|
+
raise "Required asset download failed. Build aborted."
|
|
340
344
|
end
|
|
345
|
+
end
|
|
341
346
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
347
|
+
def fetch_google_fonts
|
|
348
|
+
css_dest = "docs/fonts/fonts.css"
|
|
349
|
+
return if File.exist?(css_dest)
|
|
345
350
|
|
|
346
|
-
|
|
347
|
-
|
|
351
|
+
puts "Fetching and localizing Google Fonts..."
|
|
352
|
+
font_url = "https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
348
353
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
rescue => e
|
|
357
|
-
puts "Failed to fetch Google Fonts CSS: #{e.message}"
|
|
358
|
-
return
|
|
354
|
+
css_content = nil
|
|
355
|
+
begin
|
|
356
|
+
# Specify Chrome User-Agent to ensure Google Fonts returns modern and lightweight .woff2 formats
|
|
357
|
+
# instead of legacy formats (like .ttf or .eot) designed for older browsers
|
|
358
|
+
# rubocop:disable Security/Open
|
|
359
|
+
URI.open(font_url, "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36") do |f|
|
|
360
|
+
css_content = f.read
|
|
359
361
|
end
|
|
362
|
+
# rubocop:enable Security/Open
|
|
363
|
+
rescue StandardError => e
|
|
364
|
+
puts "Failed to fetch Google Fonts CSS: #{e.message}"
|
|
365
|
+
return
|
|
366
|
+
end
|
|
360
367
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
urls.uniq.each do |url|
|
|
364
|
-
filename = url.split("/").last
|
|
365
|
-
local_path = "docs/fonts/#{filename}"
|
|
366
|
-
download_file(url, local_path)
|
|
367
|
-
css_content.gsub!(url, filename)
|
|
368
|
-
end
|
|
368
|
+
urls = css_content.scan(%r{url\((https://fonts\.gstatic\.com/[^)]+)\)}).flatten
|
|
369
369
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
370
|
+
urls.uniq.each do |url|
|
|
371
|
+
filename = url.split("/").last
|
|
372
|
+
local_path = "docs/fonts/#{filename}"
|
|
373
|
+
download_file(url, local_path)
|
|
374
|
+
css_content.gsub!(url, filename)
|
|
373
375
|
end
|
|
374
376
|
|
|
377
|
+
FileUtils.mkdir_p("docs/fonts")
|
|
378
|
+
File.write(css_dest, css_content)
|
|
379
|
+
puts "Google Fonts localized successfully."
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
desc "Fetch required external assets for offline usage"
|
|
383
|
+
task :fetch_assets do
|
|
384
|
+
require "open-uri"
|
|
385
|
+
require "fileutils"
|
|
386
|
+
|
|
375
387
|
# Fetch Ruby WASI JS
|
|
376
388
|
download_file(
|
|
377
389
|
"https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.9.3-2.9.4/dist/browser.umd.js",
|
|
@@ -389,16 +401,16 @@ task :fetch_assets do
|
|
|
389
401
|
end
|
|
390
402
|
|
|
391
403
|
desc "Generate RDoc documentation including Visual Gallery"
|
|
392
|
-
task doc: [
|
|
404
|
+
task doc: %i[wasm fetch_assets] do
|
|
393
405
|
FileUtils.rm_rf("doc")
|
|
394
406
|
|
|
395
407
|
# RDoc コマンドを実行 (--exclude を指定して docs 配下のプレビュー用アセットの誤パースを回避)
|
|
396
|
-
sh "bundle exec rdoc --op doc" \
|
|
397
|
-
"
|
|
398
|
-
"
|
|
399
|
-
"
|
|
400
|
-
"
|
|
401
|
-
"
|
|
408
|
+
sh "bundle exec rdoc --op doc " \
|
|
409
|
+
"--exclude 'docs/coi-serviceworker\\.js' " \
|
|
410
|
+
"--exclude 'docs/zeta\\.js' " \
|
|
411
|
+
"--exclude 'docs/office_thread\\.js' " \
|
|
412
|
+
"--exclude 'docs/preview\\.html' " \
|
|
413
|
+
"--title 'xlsxrb Documentation' --main README.md README.md \"docs/visual/VisualGallery.md\" lib/"
|
|
402
414
|
|
|
403
415
|
# Copy visual gallery images and files so they are available in RDoc output
|
|
404
416
|
FileUtils.mkdir_p("doc/test/visual/baselines")
|
|
@@ -435,13 +447,14 @@ task doc: [:wasm, :fetch_assets] do
|
|
|
435
447
|
# Inject stylesheet and javascript loading tags to all generated HTML docs
|
|
436
448
|
Dir.glob("doc/**/*.html").each do |html_path|
|
|
437
449
|
next if File.basename(html_path) == "preview.html"
|
|
450
|
+
|
|
438
451
|
html_content = File.read(html_path)
|
|
439
452
|
depth = html_path.sub(%r{\Adoc/}, "").count("/")
|
|
440
453
|
rel_prefix = "../" * depth
|
|
441
454
|
|
|
442
|
-
coi_tag = %
|
|
443
|
-
js_tag = %
|
|
444
|
-
css_tag = %
|
|
455
|
+
coi_tag = %(<script src="#{rel_prefix}coi-serviceworker.js"></script>)
|
|
456
|
+
js_tag = %(<script src="#{rel_prefix}js/wasm_doc_helper.js" defer></script>)
|
|
457
|
+
css_tag = %(<link href="#{rel_prefix}css/wasm_doc_helper.css" rel="stylesheet">)
|
|
445
458
|
|
|
446
459
|
if html_content.include?("<body")
|
|
447
460
|
modified = html_content.sub("<body", "#{coi_tag}\n#{js_tag}\n#{css_tag}\n<body")
|
|
@@ -467,15 +480,17 @@ namespace :doc do
|
|
|
467
480
|
AccessLog: [],
|
|
468
481
|
# If the file is a Brotli-compressed Wasm/Data asset (checked via magic bytes),
|
|
469
482
|
# dynamically inject 'Content-Encoding: br' header so the browser decompresses it natively.
|
|
470
|
-
RequestCallback:
|
|
483
|
+
RequestCallback: lambda { |req, res|
|
|
471
484
|
if req.path.end_with?(".wasm") || req.path.end_with?(".data")
|
|
472
485
|
# Resolve physical file path from req.path manually since res.filename is nil at this stage
|
|
473
486
|
local_path = File.join(File.expand_path("doc", __dir__), req.path)
|
|
474
487
|
if File.exist?(local_path)
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
488
|
+
first_bytes = begin
|
|
489
|
+
File.binread(local_path, 4)
|
|
490
|
+
rescue StandardError
|
|
491
|
+
nil
|
|
478
492
|
end
|
|
493
|
+
res["Content-Encoding"] = "br" if first_bytes != "\x00asm"
|
|
479
494
|
end
|
|
480
495
|
end
|
|
481
496
|
}
|
data/benchmark.rb
CHANGED
data/lib/xlsxrb/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xlsxrb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- niku
|
|
@@ -37,34 +37,6 @@ dependencies:
|
|
|
37
37
|
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '0'
|
|
40
|
-
- !ruby/object:Gem::Dependency
|
|
41
|
-
name: ruby_wasm
|
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
|
43
|
-
requirements:
|
|
44
|
-
- - ">="
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: '0'
|
|
47
|
-
type: :development
|
|
48
|
-
prerelease: false
|
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
-
requirements:
|
|
51
|
-
- - ">="
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0'
|
|
54
|
-
- !ruby/object:Gem::Dependency
|
|
55
|
-
name: webrick
|
|
56
|
-
requirement: !ruby/object:Gem::Requirement
|
|
57
|
-
requirements:
|
|
58
|
-
- - ">="
|
|
59
|
-
- !ruby/object:Gem::Version
|
|
60
|
-
version: '0'
|
|
61
|
-
type: :development
|
|
62
|
-
prerelease: false
|
|
63
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - ">="
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: '0'
|
|
68
40
|
description: A minimal dependency XLSX reading and writing library for Ruby, relying
|
|
69
41
|
on standard libraries for core logic and opentelemetry-api for observability.
|
|
70
42
|
email:
|