Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/opal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: opal

on:
push:
branches: [ main ]
pull_request:

permissions:
contents: read

jobs:
boot-file-spec:
name: Opal boot file verification
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'recursive'

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3'
bundler-cache: true

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'

- name: Run opal boot file spec
# The spec statically verifies that lib/unitsdb/opal.rb is well-formed
# (every require resolves to a real file, no Opal-gated entry points
# leak in, and the boot file actually compiles under Opal::Builder with
# the native-only deps stubbed).
run: bundle exec rspec spec/unitsdb/unitsdb_opal_boot_spec.rb
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ gemspec
gem "canon"
gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main"
gem "nokogiri"
gem "opal", "~> 1.8"
gem "rake"
gem "rspec"
gem "rubocop"
Expand Down
4 changes: 3 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
require "bundler/gem_tasks"
require "rspec/core/rake_task"

RSpec::Core::RakeTask.new(:spec)
RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = "spec/unitsdb/**/*_spec.rb"
end

require "rubocop/rake_task"

Expand Down
43 changes: 43 additions & 0 deletions lib/unitsdb/opal.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

# Opal entry point for unitsdb.
#
# Under MRI, lib/unitsdb.rb uses autoload for lazy loading. Under Opal,
# autoload does not lazy-execute, so a single eager-require file lists
# every entry point the consumer (unitsml-ruby, plurimath-js) needs.
# Consumers add `-r unitsdb/opal` to their Opal compile command.

require "lutaml/model"
require "unitsdb"
require "unitsdb/config"
require "unitsdb/identifier"
require "unitsdb/localized_string"
require "unitsdb/symbol_presentations"
require "unitsdb/scale_properties"
require "unitsdb/unit_reference"
require "unitsdb/prefix_reference"
require "unitsdb/quantity_reference"
require "unitsdb/dimension_reference"
require "unitsdb/unit_system_reference"
require "unitsdb/scale_reference"
require "unitsdb/external_reference"
require "unitsdb/root_unit_reference"
require "unitsdb/si_derived_base"
require "unitsdb/dimension_details"
require "unitsdb/dimension"
require "unitsdb/prefix"
require "unitsdb/unit_system"
require "unitsdb/quantity"
require "unitsdb/scale"
require "unitsdb/unit"
require "unitsdb/dimensions"
require "unitsdb/prefixes"
require "unitsdb/quantities"
require "unitsdb/scales"
require "unitsdb/unit_systems"
require "unitsdb/units"
require "unitsdb/database"
require "unitsdb/qudt"
require "unitsdb/ucum"
require "unitsdb/errors"
require "unitsdb/utils"
57 changes: 57 additions & 0 deletions spec/unitsdb/unitsdb_opal_boot_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe Unitsdb do
subject(:boot_file) do
File.read(File.expand_path("../../lib/unitsdb/opal.rb", __dir__))
end

let(:required_paths) do
boot_file.scan(/^require "unitsdb\/([^"]+)"/).flatten
end

let(:available_paths) do
Dir[File.expand_path("../../lib/unitsdb/*.rb", __dir__)]
.map { |f| File.basename(f, ".rb") }
end

let(:skipped) do
# "opal" is this file itself; "cli" and "commands" are native-only.
# "version" is loaded by the gemspec (not by lib/unitsdb.rb), so under
# Opal the consumer is responsible for requiring it via the gem runtime.
%w[opal cli commands version]
end

describe "opal boot file at lib/unitsdb/opal.rb" do
it "eager-requires every unitsdb entry point that lib/unitsdb.rb exposes" do
expected = available_paths - skipped
missing = expected - required_paths
expect(missing).to be_empty,
"boot file is missing eager requires for: #{missing.inspect}"
end

it "does not eager-require Opal-gated entry points" do
expect(required_paths).not_to include("cli", "commands")
end

it "only references files that exist on disk" do
missing = required_paths - available_paths
expect(missing).to be_empty,
"boot file references non-existent files: #{missing.inspect}"
end

it "compiles under Opal when external deps are stubbed" do
require "opal"
require "opal/builder"

builder = Opal::Builder.new
builder.append_paths(File.expand_path("../../lib", __dir__))
# Stub native-only deps that have no Opal-compatible build at this layer.
# The gem's Opal consumer (plurimath-js) provides these.
builder.stubs += %w[lutaml/model]

expect { builder.build("unitsdb/opal") }.not_to raise_error
end
end
end
Loading