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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 985967acd75137e7175ca5ed93493f4fcc001696499e1afdfe0c9d4e5fbaad97
4
- data.tar.gz: f59bddd81cc312ecda261ef6875daffdfeff6571650298aa0cd56956318db07a
3
+ metadata.gz: 87ffb5143d51491cc1a7389a3aa673c40cc02e4e5ee69c554b795754f707c3cd
4
+ data.tar.gz: 1581c2a99238745d5e6cf65700cd3defbe8b3a3afadb55e74a44b647294407b2
5
5
  SHA512:
6
- metadata.gz: c88eb2065c70cf4d0fab24508fc168601483fdade7e81593a186272d7fd11de72f9898f178ff591f466d4d3abd14c885c6193238572256a3a08eee56073355d1
7
- data.tar.gz: a7c80be810fb05b948680492fb917f38f362460f1c095962fc090ed3d399317f83466c47f8b7ad3229c8a3bfa013a6ed23daaa174ef5d9278ef7ccf3ab36f6d8
6
+ metadata.gz: 2d994013d341f16ab98f33a190f58fd401247813a9451254eaa46809610e45a2840dc13d9e66b2b5d8c8edf0af6207eab38c9a3d0a2184fa781ff8a80dc63ed2
7
+ data.tar.gz: 9e92cd503f6a64dcdcf804996e85af09e2a11b4ceebb4586a6d0a688af81ec4cf66f7be0c86fced3acc90f76ca60dd26123aa1440b49740b1bc9522399877974
@@ -40,6 +40,7 @@ RUN apt-get update && apt-get install -y \
40
40
  imagemagick \
41
41
  fonts-liberation \
42
42
  fonts-noto-cjk \
43
+ brotli \
43
44
  && apt-get clean && rm -rf /var/lib/apt/lists/*
44
45
 
45
46
  # Pre-create the workspace folder and assign ownership to the vscode user
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
- if path
263
- dest_path = File.join(bundle_assets_dir, name)
264
- FileUtils.mkdir_p(File.dirname(dest_path))
265
- FileUtils.cp(File.join(path, name), dest_path)
266
- end
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
- desc "Fetch required external assets for offline usage"
295
- task :fetch_assets do
296
- require 'open-uri'
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
- def download_file(url, dest)
300
- # Check if the file exists and is not a tiny placeholder/error document
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
- puts "Downloading #{url} to #{dest}..."
304
- FileUtils.mkdir_p(File.dirname(dest))
299
+ require "net/http"
300
+ uri = URI.parse(url)
301
+ temp_dest = "#{dest}.tmp"
305
302
 
306
- require 'net/http'
307
- uri = URI.parse(url)
308
- temp_dest = dest + ".tmp"
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
- begin
311
- Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https', open_timeout: 15, read_timeout: 90) do |http|
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
- File.open(temp_dest, "wb") do |output|
325
- response.read_body do |chunk|
326
- output.write(chunk)
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
- def fetch_google_fonts
343
- css_dest = "docs/fonts/fonts.css"
344
- return if File.exist?(css_dest)
347
+ def fetch_google_fonts
348
+ css_dest = "docs/fonts/fonts.css"
349
+ return if File.exist?(css_dest)
345
350
 
346
- puts "Fetching and localizing Google Fonts..."
347
- font_url = "https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
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
- css_content = nil
350
- begin
351
- # Specify Chrome User-Agent to ensure Google Fonts returns modern and lightweight .woff2 formats
352
- # instead of legacy formats (like .ttf or .eot) designed for older browsers
353
- 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|
354
- css_content = f.read
355
- end
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
- urls = css_content.scan(/url\((https:\/\/fonts\.gstatic\.com\/[^\)]+)\)/).flatten
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
- FileUtils.mkdir_p("docs/fonts")
371
- File.write(css_dest, css_content)
372
- puts "Google Fonts localized successfully."
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: [:wasm, :fetch_assets] do
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
- " --exclude 'docs/coi-serviceworker\\.js'" \
398
- " --exclude 'docs/zeta\\.js'" \
399
- " --exclude 'docs/office_thread\\.js'" \
400
- " --exclude 'docs/preview\\.html'" \
401
- " --title 'xlsxrb Documentation' --main README.md README.md \"docs/visual/VisualGallery.md\" lib/"
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 = %Q{<script src="#{rel_prefix}coi-serviceworker.js"></script>}
443
- js_tag = %Q{<script src="#{rel_prefix}js/wasm_doc_helper.js" defer></script>}
444
- css_tag = %Q{<link href="#{rel_prefix}css/wasm_doc_helper.css" rel="stylesheet">}
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: ->(req, res) {
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
- first_4 = File.binread(local_path, 4) rescue nil
476
- if first_4 != "\x00asm"
477
- res['Content-Encoding'] = 'br'
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
@@ -194,6 +194,7 @@ end
194
194
 
195
195
  def median(array)
196
196
  return 0.0 if array.empty?
197
+
197
198
  sorted = array.sort
198
199
  len = sorted.length
199
200
  (sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Xlsxrb
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
5
5
  end
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.3
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: