moxml 0.1.23 → 0.1.25

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +1 -1
  3. data/.github/workflows/opal.yml +18 -1
  4. data/.github/workflows/rake.yml +32 -3
  5. data/.github/workflows/round-trip.yml +72 -9
  6. data/.gitmodules +6 -0
  7. data/.rubocop_todo.yml +2 -0
  8. data/Gemfile +9 -1
  9. data/README.adoc +124 -0
  10. data/Rakefile +117 -5
  11. data/lib/compat/opal/moxml_boot.rb +57 -0
  12. data/lib/moxml/adapter/base.rb +5 -5
  13. data/lib/moxml/adapter/customized_libxml/cdata.rb +0 -2
  14. data/lib/moxml/adapter/customized_libxml/comment.rb +0 -2
  15. data/lib/moxml/adapter/customized_libxml/element.rb +3 -5
  16. data/lib/moxml/adapter/customized_libxml/node.rb +2 -2
  17. data/lib/moxml/adapter/customized_libxml/processing_instruction.rb +0 -2
  18. data/lib/moxml/adapter/customized_libxml/text.rb +0 -2
  19. data/lib/moxml/adapter/customized_rexml/formatter.rb +3 -4
  20. data/lib/moxml/adapter/headed_ox.rb +1 -5
  21. data/lib/moxml/adapter/libxml.rb +12 -10
  22. data/lib/moxml/adapter/nokogiri.rb +4 -2
  23. data/lib/moxml/adapter/oga.rb +10 -10
  24. data/lib/moxml/adapter/ox.rb +29 -16
  25. data/lib/moxml/adapter/rexml.rb +5 -6
  26. data/lib/moxml/adapter.rb +17 -7
  27. data/lib/moxml/attribute.rb +5 -2
  28. data/lib/moxml/config.rb +2 -1
  29. data/lib/moxml/context.rb +8 -8
  30. data/lib/moxml/declaration.rb +2 -7
  31. data/lib/moxml/document.rb +0 -11
  32. data/lib/moxml/document_builder.rb +12 -8
  33. data/lib/moxml/element.rb +4 -4
  34. data/lib/moxml/node.rb +37 -14
  35. data/lib/moxml/node_set.rb +2 -4
  36. data/lib/moxml/sax/block_handler.rb +0 -2
  37. data/lib/moxml/sax/element_handler.rb +0 -2
  38. data/lib/moxml/sax/namespace_splitter.rb +5 -4
  39. data/lib/moxml/sax.rb +4 -25
  40. data/lib/moxml/version.rb +1 -1
  41. data/lib/moxml/xml_utils.rb +1 -3
  42. data/lib/moxml/xpath/compiler.rb +1 -49
  43. data/lib/moxml/xpath/conversion.rb +7 -6
  44. data/lib/moxml/xpath/ruby/generator.rb +12 -19
  45. data/lib/moxml/xpath/ruby/node.rb +1 -9
  46. data/lib/moxml/xpath.rb +6 -14
  47. data/lib/moxml.rb +67 -20
  48. data/moxml.gemspec +1 -1
  49. data/spec/moxml/adapter/platform_spec.rb +64 -46
  50. data/spec/moxml/attribute_spec.rb +16 -0
  51. data/spec/moxml/context_spec.rb +14 -0
  52. data/spec/moxml/moxml_spec.rb +13 -0
  53. data/spec/moxml/node_spec.rb +58 -0
  54. data/spec/moxml/opal_oga_adapter_spec.rb +14 -0
  55. data/spec/moxml/opal_oga_features_spec.rb +212 -0
  56. data/spec/moxml/opal_oga_smoke_spec.rb +35 -0
  57. data/spec/moxml/xpath/functions/node_functions_spec.rb +1 -1
  58. data/spec/moxml/xpath/ruby/node_spec.rb +3 -3
  59. data/spec/spec_helper.rb +1 -1
  60. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 747d843c7a968e3a25871ac9e5c67dead62e5a494a23eed41af6e157df9d66e4
4
- data.tar.gz: a636fb5fc49e0c5ebcda6a16484e2685957d9e8666b0bf043d16e5f84bcb0e57
3
+ metadata.gz: ed30996109d8cfb8057f216677cba8ca3cd3856287c38b6a7edab3251ebb2064
4
+ data.tar.gz: f93d7c13863ef9d437e40403e4a48434ed1e1c5530ea2424cdeff710740243a0
5
5
  SHA512:
6
- metadata.gz: 96248578b7b4294981437cd73baa349c99f9b01a259800c3ac48662e84fdfe4cc84fc34eef4f3d47e7f7be5517c3e4fa895e48021cd73166dcaf9867533a6461
7
- data.tar.gz: 733f99bd21656a83e17b8b6d52d08d12a19c7425364d2c8266cc2033962a8f506f8f0f603e0cdf5f0e17843dbf8b6a785b1e4462aef82c7aa173f4a8dc3d0ea2
6
+ metadata.gz: 9b09c144a268507bb02a16a4c6ba7caf6d1a2fa003718a6fa0960a8b68ba03a09584a66fb9475b1c23cd2d79071f1cc8c313200749f997d317aa90e61fba3811
7
+ data.tar.gz: 6389858a232d82faad5f6bbfd22d4828801c7a6d45a55a2a0f08d28c44647a588b52d2106649d408777453b571815b40c191f48c374cc49b648c58ad2be1e835
@@ -21,7 +21,7 @@ jobs:
21
21
  runs-on: ubuntu-latest
22
22
  steps:
23
23
  - name: Checkout
24
- uses: actions/checkout@v4
24
+ uses: actions/checkout@v6
25
25
 
26
26
  - name: Setup Ruby
27
27
  uses: ruby/setup-ruby@v1
@@ -21,16 +21,33 @@ jobs:
21
21
  uses: ruby/setup-ruby@v1
22
22
  with:
23
23
  ruby-version: "3.4"
24
- bundler-cache: true
24
+ # NOTE: bundler-cache is disabled because the path-source oga
25
+ # and ruby-ll forks need ragel + ruby-ll outputs generated
26
+ # (rake vendor:prepare) before their C extensions can compile.
27
+ bundler-cache: false
25
28
 
26
29
  - name: Set up Node.js
27
30
  uses: actions/setup-node@v4
28
31
  with:
29
32
  node-version: "18"
30
33
 
34
+ - name: Install ragel
35
+ run: sudo apt-get update && sudo apt-get install -y ragel
36
+
37
+ - name: Install ruby-ll (for rake vendor:prepare)
38
+ run: gem install ruby-ll --no-document
39
+
40
+ - name: Generate ragel / ruby-ll outputs in vendored forks
41
+ # Run before `bundle install`: the path-source oga/ruby-ll forks
42
+ # gitignore their generated .rb/.c files (parser.rb, lexer.rb,
43
+ # ext/c/lexer.c). bundle install compiles the C extensions via
44
+ # each fork's extconf.rb, which requires ext/c/lexer.c to exist.
45
+ run: rake vendor:prepare
46
+
31
47
  - name: Install Opal dependencies
32
48
  run: |
33
49
  npm list -g opal-compiler || npm install -g opal-compiler
50
+ bundle install
34
51
  bundle exec rake opal:generate_entity_data
35
52
 
36
53
  - name: Run Opal tests
@@ -13,14 +13,43 @@ jobs:
13
13
  rake:
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
- - uses: actions/checkout@v4
17
- - uses: ruby/setup-ruby@v1
16
+ - uses: actions/checkout@v6
17
+ with:
18
+ submodules: "recursive"
19
+
20
+ - name: Set up Ruby
21
+ uses: ruby/setup-ruby@v1
18
22
  with:
19
23
  ruby-version: "3.3"
20
- bundler-cache: true
24
+ # bundler-cache is disabled because the path-source oga
25
+ # and ruby-ll forks need ragel + ruby-ll outputs generated
26
+ # (rake vendor:prepare) before their C extensions can compile.
27
+ bundler-cache: false
28
+
29
+ - name: Install ragel
30
+ run: sudo apt-get update && sudo apt-get install -y ragel
31
+
32
+ - name: Install ruby-ll (for rake vendor:prepare)
33
+ run: gem install ruby-ll --no-document
34
+
35
+ - name: Generate ragel / ruby-ll outputs in vendored forks
36
+ # Run before `bundle install`: the path-source oga/ruby-ll forks
37
+ # gitignore their generated .rb/.c files (parser.rb, lexer.rb,
38
+ # ext/c/lexer.c). bundle install compiles the C extensions via
39
+ # each fork's extconf.rb, which requires ext/c/lexer.c to exist.
40
+ run: rake vendor:prepare
41
+
21
42
  - name: Install dependencies
22
43
  run: bundle install
44
+
45
+ - name: Compile liboga / libll native extensions
46
+ # Bundler does not reliably build path-source gems' native
47
+ # extensions; compile them explicitly so `require "oga"` (which
48
+ # requires libll) resolves under CRuby.
49
+ run: rake vendor:compile
50
+
23
51
  - name: Run fast tests (unit + adapter + integration)
24
52
  run: bundle exec rake spec:fast
53
+
25
54
  - name: Run rubocop
26
55
  run: bundle exec rubocop
@@ -19,11 +19,34 @@ jobs:
19
19
  ruby: [ "3.1", "3.2", "3.3", "3.4", "4.0" ]
20
20
  category: [ metanorma, rfcxml, niso-jats ]
21
21
  steps:
22
- - uses: actions/checkout@v4
23
- - uses: ruby/setup-ruby@v1
22
+ - uses: actions/checkout@v6
23
+ with:
24
+ submodules: "recursive"
25
+
26
+ - name: Set up Ruby
27
+ uses: ruby/setup-ruby@v1
24
28
  with:
25
29
  ruby-version: ${{ matrix.ruby }}
26
- bundler-cache: true
30
+ # bundler-cache is disabled because the path-source oga
31
+ # and ruby-ll forks need ragel + ruby-ll outputs generated
32
+ # (rake vendor:prepare) before their C extensions can compile.
33
+ bundler-cache: false
34
+
35
+ - name: Install ragel
36
+ run: sudo apt-get update && sudo apt-get install -y ragel
37
+
38
+ - name: Install ruby-ll (for rake vendor:prepare)
39
+ run: gem install ruby-ll --no-document
40
+
41
+ - name: Generate ragel / ruby-ll outputs in vendored forks
42
+ run: rake vendor:prepare
43
+
44
+ - name: Install dependencies
45
+ run: bundle install
46
+
47
+ - name: Compile liboga / libll native extensions
48
+ run: rake vendor:compile
49
+
27
50
  - name: Run round-trip tests (${{ matrix.category }})
28
51
  run: bundle exec rspec spec/consistency/ --tag round_trip --tag fixture_category:${{ matrix.category }}
29
52
  env:
@@ -39,11 +62,31 @@ jobs:
39
62
  ruby: [ "3.3", "4.0" ]
40
63
  category: [ metanorma, rfcxml, niso-jats ]
41
64
  steps:
42
- - uses: actions/checkout@v4
43
- - uses: ruby/setup-ruby@v1
65
+ - uses: actions/checkout@v6
66
+ with:
67
+ submodules: "recursive"
68
+
69
+ - name: Set up Ruby
70
+ uses: ruby/setup-ruby@v1
44
71
  with:
45
72
  ruby-version: ${{ matrix.ruby }}
46
- bundler-cache: true
73
+ bundler-cache: false
74
+
75
+ - name: Install ragel
76
+ run: sudo apt-get update && sudo apt-get install -y ragel
77
+
78
+ - name: Install ruby-ll (for rake vendor:prepare)
79
+ run: gem install ruby-ll --no-document
80
+
81
+ - name: Generate ragel / ruby-ll outputs in vendored forks
82
+ run: rake vendor:prepare
83
+
84
+ - name: Install dependencies
85
+ run: bundle install
86
+
87
+ - name: Compile liboga / libll native extensions
88
+ run: rake vendor:compile
89
+
47
90
  - name: Run Nokogiri × Ox round-trip tests (${{ matrix.category }})
48
91
  run: bundle exec rspec spec/consistency/ --tag round_trip --tag fixture_category:${{ matrix.category }}
49
92
  env:
@@ -61,11 +104,31 @@ jobs:
61
104
  ruby: [ "3.3", "4.0" ]
62
105
  category: [ metanorma, rfcxml, niso-jats ]
63
106
  steps:
64
- - uses: actions/checkout@v4
65
- - uses: ruby/setup-ruby@v1
107
+ - uses: actions/checkout@v6
108
+ with:
109
+ submodules: "recursive"
110
+
111
+ - name: Set up Ruby
112
+ uses: ruby/setup-ruby@v1
66
113
  with:
67
114
  ruby-version: ${{ matrix.ruby }}
68
- bundler-cache: true
115
+ bundler-cache: false
116
+
117
+ - name: Install ragel
118
+ run: sudo apt-get update && sudo apt-get install -y ragel
119
+
120
+ - name: Install ruby-ll (for rake vendor:prepare)
121
+ run: gem install ruby-ll --no-document
122
+
123
+ - name: Generate ragel / ruby-ll outputs in vendored forks
124
+ run: rake vendor:prepare
125
+
126
+ - name: Install dependencies
127
+ run: bundle install
128
+
129
+ - name: Compile liboga / libll native extensions
130
+ run: rake vendor:compile
131
+
69
132
  - name: Run Nokogiri × REXML round-trip tests (${{ matrix.category }})
70
133
  run: bundle exec rspec spec/consistency/ --tag round_trip --tag fixture_category:${{ matrix.category }}
71
134
  env:
data/.gitmodules ADDED
@@ -0,0 +1,6 @@
1
+ [submodule "opal-oga"]
2
+ path = vendor/opal-oga
3
+ url = git@github.com:lutaml/opal-oga.git
4
+ [submodule "opal-ruby-ll"]
5
+ path = vendor/opal-ruby-ll
6
+ url = git@github.com:lutaml/opal-ruby-ll.git
data/.rubocop_todo.yml CHANGED
@@ -675,6 +675,8 @@ RSpec/SpecFilePathFormat:
675
675
  - '**/spec/routing/**/*'
676
676
  - 'spec/moxml/node_type_map_spec.rb'
677
677
  - 'spec/moxml/opal_rexml_adapter_spec.rb'
678
+ - 'spec/moxml/opal_oga_adapter_spec.rb'
679
+ - 'spec/moxml/adapter/platform_spec.rb'
678
680
  - 'spec/moxml/xpath/ast/node_spec.rb'
679
681
  - 'spec/moxml/xpath/cache_spec.rb'
680
682
  - 'spec/moxml/xpath/compiler_spec.rb'
data/Gemfile CHANGED
@@ -11,11 +11,19 @@ gem "benchmark-ips"
11
11
  gem "get_process_mem"
12
12
  gem "libxml-ruby", "~> 5.0"
13
13
  gem "nokogiri", "~> 1.18"
14
- gem "oga", "~> 3.4"
15
14
  gem "openssl", "~> 3.0"
16
15
  gem "ox", "~> 2.14"
17
16
  gem "rake"
18
17
  gem "rexml"
18
+
19
+ # Opal-compatible forks of oga and ruby-ll. The forks add pure-Ruby lexer
20
+ # and driver fallbacks (under ext/pureruby/) plus an Opal-aware conditional
21
+ # in lib/oga.rb / lib/ll/setup.rb that selects the pure-Ruby implementation
22
+ # when RUBY_PLATFORM == 'opal'. Under CRuby/JRuby the forks behave
23
+ # identically to upstream (the conditional falls through to liboga/libll).
24
+ gem "oga", path: "vendor/opal-oga"
25
+ gem "ruby-ll", path: "vendor/opal-ruby-ll"
26
+
19
27
  gem "rspec"
20
28
  gem "rubocop"
21
29
  gem "rubocop-performance"
data/README.adoc CHANGED
@@ -889,6 +889,130 @@ performance optimization, and testing strategies, see
889
889
  link:docs/_pages/best-practices.adoc[Best Practices Guide].
890
890
 
891
891
 
892
+ == Running under Opal (JavaScript)
893
+
894
+ Opal compiles Ruby to JavaScript and cannot load C extensions. To run
895
+ Moxml on Opal you need three things: an adapter with no native code, a
896
+ XML library fork that exposes pure-Ruby fallbacks for its lexer and
897
+ parser driver, and an Opal build that puts those pure-Ruby
898
+ implementations on the load path.
899
+
900
+ === Default adapter under Opal
901
+
902
+ Under `RUBY_ENGINE == "opal"`, Moxml's default adapter is `:oga` (see
903
+ <<Default adapter selection>>). Oga is a pure-Ruby XML parser; under
904
+ standard CRuby its lexer and parsers are accelerated by the `liboga`
905
+ and `libll` C extensions. Those extensions cannot be compiled into
906
+ JavaScript, so on Opal they are replaced by the pure-Ruby fallbacks
907
+ shipped in `ext/pureruby/` of the Opal-compatible forks below.
908
+
909
+ The REXML adapter is also available on Opal (`Moxml.new(:rexml)`) but
910
+ relies on a number of compatibility shims because REXML uses Ruby
911
+ regular-expression features (the `/n` flag, `\u{...}` codepoint
912
+ escapes, inline flag groups) and String APIs that do not all transpile
913
+ to JavaScript today. `:oga` is the recommended path for new Opal
914
+ projects.
915
+
916
+ === Required XML library forks
917
+
918
+ Upstream https://github.com/yorickpeterse/oga[oga] and
919
+ https://github.com/yorickpeterse/ruby-ll[ruby-ll] do not currently
920
+ ship Opal-compatible fallbacks. Moxml vendors the following forks:
921
+
922
+ * https://github.com/lutaml/opal-oga[lutaml/opal-oga] — oga with an
923
+ `ext/pureruby/oga/native/lexer.rb` fallback that fires when
924
+ `RUBY_PLATFORM == 'opal'`. Under CRuby/JRuby the fork behaves
925
+ identically to upstream oga.
926
+ * https://github.com/lutaml/opal-ruby-ll[lutaml/opal-ruby-ll] —
927
+ ruby-ll with `ext/pureruby/ll/native/{driver,driver_config}.rb`
928
+ fallbacks selected by the same conditional in `lib/ll/setup.rb`.
929
+
930
+ Both forks are vendored as submodules under `vendor/opal-oga` and
931
+ `vendor/opal-ruby-ll` in the Moxml repository. Moxml's own `Gemfile`
932
+ references them via `path:` source so that any consumer of Moxml under
933
+ Opal resolves to the same fork commits Moxml was tested against.
934
+
935
+ === Wiring the forks in a downstream Gemfile
936
+
937
+ Downstream libraries that run on Opal (for example
938
+ https://github.com/plurimath/plurimath-js[plurimath-js]) must also use
939
+ the Opal-compatible forks rather than upstream `oga` and `ruby-ll`.
940
+ Wire them as `path:` or `git:` sources so the fork's
941
+ `RUBY_PLATFORM == 'opal'` conditional can fire:
942
+
943
+ [source,ruby]
944
+ ----
945
+ # In your downstream gem's Gemfile (or gemspec)
946
+ gem "moxml", "~> 0.1.24" # provides the :oga adapter
947
+ gem "oga", git: "https://github.com/lutaml/opal-oga.git",
948
+ ref: "<commit-sha-moxml-tests-against>"
949
+ gem "ruby-ll", git: "https://github.com/lutaml/opal-ruby-ll.git",
950
+ ref: "<commit-sha-moxml-tests-against>"
951
+ ----
952
+
953
+ Pin the `ref:` to the submodule commits Moxml ships in
954
+ `vendor/opal-oga` and `vendor/opal-ruby-ll` (recorded in
955
+ `.gitmodules`) so your fork and Moxml's fork stay in lockstep. If you
956
+ prefer to vendor the forks yourself, replace the `git:` source with
957
+ `path: "vendor/opal-oga"` and `path: "vendor/opal-ruby-ll"` after
958
+ copying them in.
959
+
960
+ === Opal build configuration
961
+
962
+ Under Opal, the forks' `lib/` directory is on the load path (via the
963
+ gem), but the pure-Ruby implementations live under `ext/pureruby/`.
964
+ That directory must be added to Opal's load path so the
965
+ `if RUBY_PLATFORM == 'opal'` branch in `lib/oga.rb` and
966
+ `lib/ll/setup.rb` resolves to the pure-Ruby files instead of trying to
967
+ load the C extension.
968
+
969
+ For consumers using https://github.com/opal/opal[Opal] directly:
970
+
971
+ [source,ruby]
972
+ ----
973
+ require "opal"
974
+
975
+ # Opal path: put both lib/ and ext/pureruby/ on the load path so the
976
+ # fork's conditional requires resolve to the pure-Ruby fallback.
977
+ %w[opal-oga opal-ruby-ll].each do |fork|
978
+ Opal.append_path File.expand_path("vendor/#{fork}/lib", __dir__)
979
+ Opal.append_path File.expand_path("vendor/#{fork}/ext/pureruby", __dir__)
980
+ end
981
+
982
+ # Moxml's Opal boot file (lib/compat/opal/moxml_boot.rb) explicitly
983
+ # requires every module that would normally be autoloaded under CRuby.
984
+ require "moxml_boot"
985
+ ----
986
+
987
+ For consumers using https://github.com/opal/opal-compiler[Opal via
988
+ Sprockets/Webpack], the equivalent is to add both `lib/` and
989
+ `ext/pureruby/` from each fork to the Opal `paths` array.
990
+
991
+ === Verifying the Opal build
992
+
993
+ Moxml's own Opal test suite exercises the wiring described above. To
994
+ reproduce:
995
+
996
+ [source,shell]
997
+ ----
998
+ # One-time setup: install ragel + ruby-ll so the forks' gitignored
999
+ # generated lexer/parser sources can be produced.
1000
+ gem install ruby-ll --no-document
1001
+ # (Install ragel via your package manager, e.g. apt-get install ragel
1002
+ # or brew install ragel.)
1003
+
1004
+ # Generate the gitignored outputs in the vendored forks.
1005
+ bundle exec rake vendor:prepare
1006
+
1007
+ # Run the Opal (JavaScript) test suite.
1008
+ bundle exec rake spec:opal
1009
+ ----
1010
+
1011
+ Reference implementation: https://github.com/plurimath/plurimath-js[plurimath-js]
1012
+ is the canonical downstream consumer; its `Rakefile` and `env/` wrapper
1013
+ scripts show the full pattern end-to-end.
1014
+
1015
+
892
1016
  == Specific adapter limitations
893
1017
 
894
1018
  === Ox adapter
data/Rakefile CHANGED
@@ -1,9 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
4
 
6
- RSpec::Core::RakeTask.new(:spec)
5
+ # vendor:prepare must be runnable before `bundle install` (CI runs it
6
+ # first so the path-source oga/ruby-ll forks' gitignored lexer/parser
7
+ # outputs exist before their extconf.rb runs). Guard the rspec/opal
8
+ # requires so the file loads with only `rake` + `bundler` available.
9
+ begin
10
+ require "rspec/core/rake_task"
11
+ RSpec::Core::RakeTask.new(:spec)
12
+ rescue LoadError
13
+ end
7
14
 
8
15
  begin
9
16
  require "opal/rspec/rake_task"
@@ -17,15 +24,110 @@ begin
17
24
  File.exist?(File.join(p, "rexml", "document.rb"))
18
25
  end
19
26
  Opal.append_path rexml_lib if rexml_lib
27
+
28
+ # The Opal-compatible oga and ruby-ll forks (vendored as submodules)
29
+ # expose pure-Ruby implementations under ext/pureruby/. Their top-level
30
+ # lib/oga.rb and lib/ll/setup.rb conditionally require them when
31
+ # RUBY_PLATFORM == 'opal'. Both lib/ and ext/pureruby/ must be on
32
+ # Opal's load path so the conditional resolves correctly.
33
+ %w[opal-oga opal-ruby-ll].each do |fork_name|
34
+ fork_path = File.expand_path("vendor/#{fork_name}", __dir__)
35
+ Opal.append_path File.join(fork_path, "lib")
36
+ Opal.append_path File.join(fork_path, "ext/pureruby")
37
+ end
20
38
  end
21
39
  rescue LoadError
22
40
  # Opal not available or incompatible with current Ruby version
23
41
  end
24
42
 
25
- require "rubocop/rake_task"
43
+ begin
44
+ require "rubocop/rake_task"
45
+ RuboCop::RakeTask.new
46
+ rescue LoadError
47
+ end
48
+
49
+ # Regenerate the ragel / ruby-ll outputs that the Opal-compatible forks
50
+ # (vendored as submodules under vendor/) gitignore. The forks ship the
51
+ # grammar sources (.rl / .rll) but not the generated .rb / .c, since
52
+ # those are large and version-controllable upstream. Both ragel and
53
+ # ruby-ll must be on PATH; the upstream ruby-ll gem is sufficient for
54
+ # generation (the fork is only needed at runtime).
55
+ namespace :vendor do
56
+ desc "Generate ragel / ruby-ll outputs in vendored opal-oga and opal-ruby-ll"
57
+ task :prepare do
58
+ require "fileutils"
59
+
60
+ oga = File.expand_path("vendor/opal-oga", __dir__)
61
+ ruby_ll = File.expand_path("vendor/opal-ruby-ll", __dir__)
62
+
63
+ generators = [
64
+ # oga: ruby-ll grammar → Ruby parser
65
+ ["ruby-ll #{oga}/lib/oga/xml/parser.rll -o #{oga}/lib/oga/xml/parser.rb",
66
+ "#{oga}/lib/oga/xml/parser.rb",
67
+ "#{oga}/lib/oga/xml/parser.rll"],
68
+ ["ruby-ll #{oga}/lib/oga/xpath/parser.rll -o #{oga}/lib/oga/xpath/parser.rb",
69
+ "#{oga}/lib/oga/xpath/parser.rb",
70
+ "#{oga}/lib/oga/xpath/parser.rll"],
71
+ ["ruby-ll #{oga}/lib/oga/css/parser.rll -o #{oga}/lib/oga/css/parser.rb",
72
+ "#{oga}/lib/oga/css/parser.rb",
73
+ "#{oga}/lib/oga/css/parser.rll"],
74
+ # oga: ragel Ruby lexer
75
+ ["ragel -R -F1 #{oga}/lib/oga/xpath/lexer.rl -o #{oga}/lib/oga/xpath/lexer.rb",
76
+ "#{oga}/lib/oga/xpath/lexer.rb",
77
+ "#{oga}/lib/oga/xpath/lexer.rl"],
78
+ ["ragel -R -F1 #{oga}/lib/oga/css/lexer.rl -o #{oga}/lib/oga/css/lexer.rb",
79
+ "#{oga}/lib/oga/css/lexer.rb",
80
+ "#{oga}/lib/oga/css/lexer.rl"],
81
+ # oga: ragel C lexer for liboga
82
+ ["ragel -C -I #{oga}/ext/ragel -G2 #{oga}/ext/c/lexer.rl -o #{oga}/ext/c/lexer.c",
83
+ "#{oga}/ext/c/lexer.c",
84
+ "#{oga}/ext/c/lexer.rl"],
85
+ # ruby-ll: ruby-ll grammar → Ruby parser
86
+ ["ruby-ll #{ruby_ll}/lib/ll/parser.rll -o #{ruby_ll}/lib/ll/parser.rb --no-requires",
87
+ "#{ruby_ll}/lib/ll/parser.rb",
88
+ "#{ruby_ll}/lib/ll/parser.rll"],
89
+ ]
26
90
 
27
- RuboCop::RakeTask.new
91
+ generators.each do |cmd, output, source|
92
+ if File.exist?(output) && File.mtime(output) >= File.mtime(source)
93
+ next
94
+ end
28
95
 
96
+ FileUtils.mkdir_p(File.dirname(output))
97
+ sh cmd
98
+ end
99
+ end
100
+
101
+ # Bundler does not reliably compile native extensions for path-source
102
+ # gems. Build liboga/libll explicitly so `require "oga"` resolves.
103
+ desc "Compile liboga / libll native extensions for vendored forks"
104
+ task :compile do
105
+ require "fileutils"
106
+ require "rbconfig"
107
+
108
+ dlext = RbConfig::CONFIG["DLEXT"]
109
+
110
+ {
111
+ "vendor/opal-ruby-ll" => "libll",
112
+ "vendor/opal-oga" => "liboga",
113
+ }.each do |fork_path, ext_name|
114
+ abs_fork = File.expand_path(fork_path, __dir__)
115
+ lib_bundle = File.join(abs_fork, "lib", "#{ext_name}.#{dlext}")
116
+ ext_dir = File.join(abs_fork, "ext", "c")
117
+ extconf = File.join(ext_dir, "extconf.rb")
118
+ lib_dir = File.join(abs_fork, "lib")
119
+ next if File.exist?(lib_bundle)
120
+
121
+ Dir.chdir(ext_dir) do
122
+ sh "ruby #{extconf}"
123
+ sh "make"
124
+ FileUtils.cp("#{ext_name}.#{dlext}", lib_dir)
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ if defined?(RSpec)
29
131
  namespace :spec do
30
132
  if defined?(Opal::RSpec::RakeTask)
31
133
  desc "Run Opal (JavaScript) tests"
@@ -34,12 +136,21 @@ namespace :spec do
34
136
  server.append_path "spec"
35
137
 
36
138
  runner.default_path = "spec"
139
+ # `oga` and `ll/setup` must be required before moxml_boot so that
140
+ # the forks' Opal-aware conditional requires fire (lib/oga.rb calls
141
+ # `require 'oga/native/lexer'` when RUBY_PLATFORM == 'opal'; that
142
+ # resolves against vendor/opal-oga/ext/pureruby/, which the global
143
+ # Opal.append_path calls above add to the load path).
37
144
  runner.requires = %w[rexml_compat rexml/document rexml/xpath
38
- moxml/adapter/rexml spec_helper support/opal]
145
+ oga ll/setup
146
+ moxml_boot spec_helper support/opal]
39
147
  runner.files = Dir.glob("spec/moxml/*opal*_spec.rb") +
40
148
  Dir.glob("spec/moxml/native_attachment/opal_spec.rb") +
41
149
  Dir.glob("spec/moxml/adapter/shared_examples/*.rb")
42
150
  end
151
+
152
+ desc "Alias for spec:opal that also runs vendor:prepare"
153
+ task opal: "vendor:prepare"
43
154
  end
44
155
 
45
156
  desc "Validate XML fixtures are well-formed (requires xmllint)"
@@ -124,6 +235,7 @@ namespace :spec do
124
235
  task all: %i[unit adapter integration consistency examples
125
236
  performance]
126
237
  end
238
+ end
127
239
 
128
240
  namespace :benchmark do
129
241
  desc "Run XPath performance benchmarks"
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Opal doesn't support Ruby autoload. This file explicitly requires
4
+ # all Moxml modules that would normally be lazy-loaded via autoload
5
+ # in lib/moxml.rb. Loaded by the Opal Rakefile before spec_helper.
6
+
7
+ require "moxml/version"
8
+ require "moxml/error"
9
+ require "moxml/native_attachment"
10
+ require "moxml/native_attachment/opal"
11
+ require "moxml/xml_utils"
12
+ require "moxml/xml_utils/encoder"
13
+ require "moxml/node"
14
+ require "moxml/node_set"
15
+ require "moxml/document"
16
+ require "moxml/element"
17
+ require "moxml/attribute"
18
+ require "moxml/text"
19
+ require "moxml/cdata"
20
+ require "moxml/comment"
21
+ require "moxml/processing_instruction"
22
+ require "moxml/declaration"
23
+ require "moxml/namespace"
24
+ require "moxml/doctype"
25
+ require "moxml/entity_reference"
26
+ require "moxml/entity_registry"
27
+ require "moxml/adapter"
28
+ require "moxml/adapter/base"
29
+ require "moxml/adapter/customized_rexml"
30
+ require "moxml/adapter/customized_rexml/entity_reference"
31
+ require "moxml/adapter/customized_rexml/formatter"
32
+ require "moxml/sax"
33
+ require "moxml/sax/handler"
34
+ require "moxml/sax/element_handler"
35
+ require "moxml/sax/block_handler"
36
+ require "moxml/sax/namespace_splitter"
37
+ require "moxml/adapter/rexml"
38
+ require "moxml/adapter/customized_oga"
39
+ require "moxml/adapter/customized_oga/xml_declaration"
40
+ require "moxml/adapter/customized_oga/xml_generator"
41
+ require "moxml/adapter/oga"
42
+ require "moxml/document_builder"
43
+ require "moxml/builder"
44
+ require "moxml/context"
45
+ require "moxml/config"
46
+ require "moxml/xpath"
47
+ require "moxml/xpath/engine"
48
+ require "moxml/xpath/context"
49
+ require "moxml/xpath/conversion"
50
+ require "moxml/xpath/cache"
51
+ require "moxml/xpath/lexer"
52
+ require "moxml/xpath/parser"
53
+ require "moxml/xpath/compiler"
54
+ require "moxml/xpath/errors"
55
+ require "moxml/xpath/ast/node"
56
+ require "moxml/xpath/ruby/node"
57
+ require "moxml/xpath/ruby/generator"
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../xml_utils"
4
- require_relative "../document_builder"
5
-
6
3
  module Moxml
7
4
  module Adapter
8
5
  class Base
@@ -104,8 +101,7 @@ module Moxml
104
101
  #
105
102
  # @return [Boolean] true if SAX parsing is supported
106
103
  def sax_supported?
107
- respond_to?(:sax_parse) &&
108
- method(:sax_parse).owner != Moxml::Adapter::Base.singleton_class
104
+ method(:sax_parse).owner != Moxml::Adapter::Base.singleton_class
109
105
  end
110
106
 
111
107
  def create_document(_native_doc = nil)
@@ -212,6 +208,10 @@ namespace_validation_mode: :strict)
212
208
  wrapper.has_xml_declaration
213
209
  end
214
210
 
211
+ # Clear the declaration state from the native document.
212
+ # Called when a Declaration node is removed from a document.
213
+ def remove_declaration(_native_doc); end
214
+
215
215
  # Return the actual native node after an add_child operation.
216
216
  # Override for adapters where node identity may change (e.g., LibXML doc.root=).
217
217
  def actual_native(child_native, _parent_native)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "node"
4
-
5
3
  module Moxml
6
4
  module Adapter
7
5
  module CustomizedLibxml
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "node"
4
-
5
3
  module Moxml
6
4
  module Adapter
7
5
  module CustomizedLibxml