serialbench 0.1.3 → 0.2.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 +4 -4
- data/.github/workflows/benchmark.yml +71 -51
- data/Gemfile +2 -0
- data/README.adoc +5 -0
- data/lib/serialbench/cli/resultset_cli.rb +40 -0
- data/lib/serialbench/models/platform.rb +5 -2
- data/lib/serialbench/serializers/json/rapidjson_serializer.rb +0 -4
- data/lib/serialbench/serializers/xml/base_xml_serializer.rb +6 -1
- data/lib/serialbench/serializers/xml/leptris_serializer.rb +167 -0
- data/lib/serialbench/serializers/xml/libxml_serializer.rb +9 -0
- data/lib/serialbench/serializers/xml/nokogiri_serializer.rb +9 -0
- data/lib/serialbench/serializers/xml/oga_serializer.rb +4 -0
- data/lib/serialbench/serializers/yaml/base_yaml_serializer.rb +1 -3
- data/lib/serialbench/serializers/yaml/psych_serializer.rb +0 -4
- data/lib/serialbench/serializers.rb +3 -1
- data/lib/serialbench/site_generator.rb +56 -2
- data/lib/serialbench/version.rb +1 -1
- data/site/.gitignore +5 -0
- data/site/README.md +32 -0
- data/site/astro.config.mjs +12 -0
- data/site/package-lock.json +6160 -0
- data/site/package.json +22 -0
- data/site/public/favicon.svg +4 -0
- data/site/scripts/gen-sample-data.mjs +154 -0
- data/site/src/components/AvailabilityMatrix.astro +61 -0
- data/site/src/components/CalibratedLeaderboard.vue +134 -0
- data/site/src/components/CitationBox.vue +50 -0
- data/site/src/components/DashboardConsole.vue +193 -0
- data/site/src/components/FeaturesTable.astro +97 -0
- data/site/src/components/MemoryPanel.vue +44 -0
- data/site/src/components/OpsChart.vue +135 -0
- data/site/src/config.ts +18 -0
- data/site/src/layouts/Layout.astro +85 -0
- data/site/src/lib/channels.ts +24 -0
- data/site/src/lib/dashboard.ts +269 -0
- data/site/src/pages/index.astro +44 -0
- data/site/src/pages/library/[slug].astro +128 -0
- data/site/src/pages/methodology.astro +55 -0
- data/site/src/styles/global.css +103 -0
- data/site/tsconfig.json +5 -0
- metadata +26 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff36b734da92061c2bbf271fd1e94f79f80d844f0b492c7f06a51aca5ed794aa
|
|
4
|
+
data.tar.gz: 28aeb4041d3c1c04cdcfd4b5f5ad21757ed0616277bcf34b2db15c9d56a06b5a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c497b089e84bc674bd444df875cc161ecf97b0d077d2ba9cb3c26cee91c27d8694cabc149b61f78c4fc0f9890b808a7604e6106e1d91aa04595c40a3cb6fa77
|
|
7
|
+
data.tar.gz: 5a8eebcfe19699b5d9e1aed3caf1c65bec4937fe51199b8da0ef152e6145deab815621c354495279236f9b7c2e0eaa8e1c70eebdc1353ffb8ff6c89c900afeaa
|
|
@@ -20,7 +20,10 @@ permissions:
|
|
|
20
20
|
id-token: write
|
|
21
21
|
|
|
22
22
|
concurrency:
|
|
23
|
-
|
|
23
|
+
# Renamed off "pages": that group's server-side state is wedged (a queued
|
|
24
|
+
# run there returns 500 on cancel and blocks the single in-progress slot),
|
|
25
|
+
# which starved every benchmark-weekly run after 2026-08-23 10:22Z.
|
|
26
|
+
group: "site-deploy"
|
|
24
27
|
cancel-in-progress: false
|
|
25
28
|
|
|
26
29
|
jobs:
|
|
@@ -33,8 +36,8 @@ jobs:
|
|
|
33
36
|
- name: Set matrix configurations
|
|
34
37
|
id: set-matrix
|
|
35
38
|
run: |
|
|
36
|
-
echo 'ruby-versions=["3.
|
|
37
|
-
echo 'platforms=["ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-
|
|
39
|
+
echo 'ruby-versions=["3.2", "3.3", "3.4", "4.0"]' >> $GITHUB_OUTPUT
|
|
40
|
+
echo 'platforms=["ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-14", "macos-15", "macos-15-intel", "macos-26", "macos-26-intel", "windows-2022", "windows-2025", "windows-11-arm"]' >> $GITHUB_OUTPUT
|
|
38
41
|
|
|
39
42
|
# Cross-platform native benchmarks
|
|
40
43
|
benchmark:
|
|
@@ -46,9 +49,8 @@ jobs:
|
|
|
46
49
|
platform: ${{ fromJson(needs.setup.outputs.platforms) }}
|
|
47
50
|
ruby-version: ${{ fromJson(needs.setup.outputs.ruby-versions) }}
|
|
48
51
|
exclude:
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
-
ruby-version: "3.1"
|
|
52
|
+
# RubyInstaller ships no arm64 builds for Ruby 3.2/3.3;
|
|
53
|
+
# windows-11-arm runs 3.4.x and 4.x only
|
|
52
54
|
- platform: windows-11-arm
|
|
53
55
|
ruby-version: "3.2"
|
|
54
56
|
- platform: windows-11-arm
|
|
@@ -72,8 +74,11 @@ jobs:
|
|
|
72
74
|
- name: Install vcpkg dependencies (Windows)
|
|
73
75
|
if: startsWith(matrix.platform, 'windows-')
|
|
74
76
|
run: |
|
|
75
|
-
vcpkg install libxml2:${{ steps.windows-arch.outputs.arch }} 2>&1 || true
|
|
77
|
+
vcpkg install libxml2:${{ steps.windows-arch.outputs.arch }} libxslt:${{ steps.windows-arch.outputs.arch }} 2>&1 || true
|
|
76
78
|
vcpkg integrate install 2>&1 || true
|
|
79
|
+
# vcpkg dynamic triplets put the DLLs in bin/; nokogiri's compiled
|
|
80
|
+
# extension needs them on PATH at require time.
|
|
81
|
+
echo "C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}/bin" >> $GITHUB_PATH
|
|
77
82
|
shell: bash
|
|
78
83
|
|
|
79
84
|
- name: Configure bundler for libxml2 (Windows)
|
|
@@ -82,6 +87,11 @@ jobs:
|
|
|
82
87
|
mkdir -p .bundle 2>&1 || true
|
|
83
88
|
echo "---" > .bundle/config 2>&1 || true
|
|
84
89
|
echo 'BUNDLE_BUILD__LIBXML___RUBY: "--with-xml2-dir=C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }} --with-xml2-include=C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}/include/libxml2 --with-xml2-lib=C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}/lib"' >> .bundle/config 2>&1 || true
|
|
90
|
+
# windows-11-arm has no precompiled nokogiri (pulled transitively via
|
|
91
|
+
# moxml); build it against the vcpkg libxml2 instead of the vendored
|
|
92
|
+
# source build that fails on ucrt/ARM64. Harmless on x64 Windows,
|
|
93
|
+
# where the precompiled gem makes this config moot.
|
|
94
|
+
echo 'BUNDLE_BUILD__NOKOGIRI: "--use-system-libraries --with-xml2-include=C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}/include/libxml2 --with-xml2-lib=C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}/lib --with-xslt-include=C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}/include/libxslt --with-xslt-lib=C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}/lib --with-zlib-dir=C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}"' >> .bundle/config 2>&1 || true
|
|
85
95
|
shell: bash
|
|
86
96
|
|
|
87
97
|
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
@@ -90,6 +100,18 @@ jobs:
|
|
|
90
100
|
ruby-version: ${{ matrix.ruby-version }}
|
|
91
101
|
bundler-cache: true
|
|
92
102
|
|
|
103
|
+
# The directory of ruby.exe is always in the DLL search path (unlike
|
|
104
|
+
# PATH manipulation across step boundaries), so stage the vcpkg DLLs
|
|
105
|
+
# there for the system-built nokogiri on Windows ARM.
|
|
106
|
+
- name: Stage vcpkg DLLs beside ruby.exe (Windows)
|
|
107
|
+
if: startsWith(matrix.platform, 'windows-')
|
|
108
|
+
run: |
|
|
109
|
+
RUBY_BIN="$(dirname "$(command -v ruby)")"
|
|
110
|
+
echo "Staging vcpkg DLLs into $RUBY_BIN"
|
|
111
|
+
ls "C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}/bin/" || true
|
|
112
|
+
cp -f "C:/vcpkg/installed/${{ steps.windows-arch.outputs.arch }}/bin/"*.dll "$RUBY_BIN/" 2>&1 || true
|
|
113
|
+
shell: bash
|
|
114
|
+
|
|
93
115
|
- name: Install system dependencies (Ubuntu)
|
|
94
116
|
if: startsWith(matrix.platform, 'ubuntu-')
|
|
95
117
|
run: |
|
|
@@ -116,6 +138,28 @@ jobs:
|
|
|
116
138
|
- name: Install gems
|
|
117
139
|
run: bundle install
|
|
118
140
|
|
|
141
|
+
# ubuntu-22.04's glibc (2.35) predates the precompiled leptris gem's
|
|
142
|
+
# build host, so the gem's vendored libleptris.so fails to dlopen there
|
|
143
|
+
# and the adapter reports leptris unavailable (empty availability dots
|
|
144
|
+
# on the site). Leptris is pure C99 — build the lib from the gem's
|
|
145
|
+
# tagged source and point the FFI loader at it; LEPTRIS_LIB_PATH is the
|
|
146
|
+
# first candidate in the gem's ffi_lib list.
|
|
147
|
+
- name: Build libleptris from source (Ubuntu 22.04)
|
|
148
|
+
if: matrix.platform == 'ubuntu-22.04' || matrix.platform == 'ubuntu-22.04-arm'
|
|
149
|
+
run: |
|
|
150
|
+
LEPTRIS_VERSION=$(bundle exec ruby -e "require 'leptris'; print Leptris::VERSION")
|
|
151
|
+
echo "Building libleptris ${LEPTRIS_VERSION}"
|
|
152
|
+
curl -sL "https://github.com/leptris/leptris/archive/refs/tags/v${LEPTRIS_VERSION}.tar.gz" | tar xz
|
|
153
|
+
cmake -S "leptris-${LEPTRIS_VERSION}" -B /tmp/leptris-build \
|
|
154
|
+
-DLEPTRIS_BUILD_SHARED=ON -DLEPTRIS_BUILD_STATIC=OFF -DBUILD_TESTING=OFF \
|
|
155
|
+
-DLEPTRIS_BUILD_CLI=OFF -DLEPTRIS_BUILD_BENCHMARKS=OFF -DLEPTRIS_BUILD_MAN_PAGES=OFF \
|
|
156
|
+
-DLEPTRIS_ENABLE_UTF8PROC=OFF -DLEPTRIS_ENABLE_ICONV=OFF
|
|
157
|
+
cmake --build /tmp/leptris-build -j"$(nproc)"
|
|
158
|
+
LIBLEPTRIS=$(find /tmp/leptris-build -name 'libleptris.so*' -type f | head -1)
|
|
159
|
+
echo "Built ${LIBLEPTRIS}"
|
|
160
|
+
echo "LEPTRIS_LIB_PATH=${LIBLEPTRIS}" >> "$GITHUB_ENV"
|
|
161
|
+
shell: bash
|
|
162
|
+
|
|
119
163
|
- name: Update Ruby-Build cache
|
|
120
164
|
run: bundle exec serialbench ruby-build update
|
|
121
165
|
env:
|
|
@@ -138,7 +182,7 @@ jobs:
|
|
|
138
182
|
env:
|
|
139
183
|
GITHUB_RUNNER_PLATFORM: ${{ matrix.platform }}
|
|
140
184
|
run: |
|
|
141
|
-
bundle exec serialbench environment execute config/environments/ci-ruby-${{ matrix.ruby-version }}.yml config/benchmarks/
|
|
185
|
+
bundle exec serialbench environment execute config/environments/ci-ruby-${{ matrix.ruby-version }}.yml config/benchmarks/full.yml results/runs/ci-ruby-${{ matrix.ruby-version }}-${{ matrix.platform }}
|
|
142
186
|
|
|
143
187
|
- name: Verify benchmark results before upload
|
|
144
188
|
run: |
|
|
@@ -230,47 +274,22 @@ jobs:
|
|
|
230
274
|
run: |
|
|
231
275
|
bundle exec serialbench resultset add-result results/sets/weekly-benchmark artifacts/benchmark-results-*/
|
|
232
276
|
|
|
233
|
-
- name:
|
|
234
|
-
run: |
|
|
235
|
-
bundle exec serialbench resultset build-site results/sets/weekly-benchmark _site
|
|
236
|
-
|
|
237
|
-
- name: Create summary page
|
|
277
|
+
- name: Export dashboard payload and raw data
|
|
238
278
|
run: |
|
|
239
|
-
|
|
240
|
-
cat > _site/README.md << 'EOF'
|
|
241
|
-
# Serialbench - Weekly Benchmark Results
|
|
242
|
-
|
|
243
|
-
This site contains automated benchmark results for Ruby serialization libraries.
|
|
244
|
-
|
|
245
|
-
## Benchmarked Libraries
|
|
246
|
-
|
|
247
|
-
- **XML**: REXML, Ox, Nokogiri, Oga, LibXML
|
|
248
|
-
- **JSON**: JSON, Oj, RapidJSON, YAJL
|
|
249
|
-
- **YAML**: Psych
|
|
250
|
-
- **TOML**: TOML-RB, Tomlib, tomlrb
|
|
279
|
+
bundle exec serialbench resultset export-data results/sets/weekly-benchmark site/src/data/sample.json --raw-data-dir site/public/data
|
|
251
280
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
-
|
|
256
|
-
|
|
257
|
-
-
|
|
258
|
-
- macOS 15 Intel (Intel x86_64)
|
|
259
|
-
- macOS 14 (Apple Silicon ARM64)
|
|
260
|
-
- macOS 15 (Apple Silicon ARM64)
|
|
261
|
-
|
|
262
|
-
## Ruby Versions
|
|
263
|
-
|
|
264
|
-
- Ruby 3.1, 3.2, 3.3, 3.4
|
|
265
|
-
|
|
266
|
-
## View Results
|
|
267
|
-
|
|
268
|
-
Open [index.html](./index.html) to view the interactive dashboard.
|
|
269
|
-
|
|
270
|
-
---
|
|
281
|
+
- name: Set up Node
|
|
282
|
+
uses: actions/setup-node@v4
|
|
283
|
+
with:
|
|
284
|
+
node-version: '24'
|
|
285
|
+
cache: npm
|
|
286
|
+
cache-dependency-path: site/package-lock.json
|
|
271
287
|
|
|
272
|
-
|
|
273
|
-
|
|
288
|
+
- name: Build site
|
|
289
|
+
run: |
|
|
290
|
+
cd site
|
|
291
|
+
npm ci
|
|
292
|
+
npm run build
|
|
274
293
|
|
|
275
294
|
- name: Setup Pages
|
|
276
295
|
uses: actions/configure-pages@v4
|
|
@@ -278,7 +297,7 @@ jobs:
|
|
|
278
297
|
- name: Upload to GitHub Pages
|
|
279
298
|
uses: actions/upload-pages-artifact@v3
|
|
280
299
|
with:
|
|
281
|
-
path:
|
|
300
|
+
path: site/dist
|
|
282
301
|
|
|
283
302
|
- name: Deploy to GitHub Pages
|
|
284
303
|
id: deployment
|
|
@@ -301,16 +320,17 @@ jobs:
|
|
|
301
320
|
echo "- Ubuntu Latest (Linux x86_64)" >> $GITHUB_STEP_SUMMARY
|
|
302
321
|
echo "- Ubuntu 24.04 (Linux ARM64)" >> $GITHUB_STEP_SUMMARY
|
|
303
322
|
echo "- Ubuntu 22.04 (Linux ARM64)" >> $GITHUB_STEP_SUMMARY
|
|
304
|
-
echo "- macOS 13 (Intel x86_64)" >> $GITHUB_STEP_SUMMARY
|
|
305
|
-
echo "- macOS 15 Intel (Intel x86_64)" >> $GITHUB_STEP_SUMMARY
|
|
306
323
|
echo "- macOS 14 (Apple Silicon ARM64)" >> $GITHUB_STEP_SUMMARY
|
|
307
324
|
echo "- macOS 15 (Apple Silicon ARM64)" >> $GITHUB_STEP_SUMMARY
|
|
325
|
+
echo "- macOS 15 Intel (Intel x86_64)" >> $GITHUB_STEP_SUMMARY
|
|
326
|
+
echo "- macOS 26 (Apple Silicon ARM64)" >> $GITHUB_STEP_SUMMARY
|
|
327
|
+
echo "- macOS 26 Intel (Intel x86_64)" >> $GITHUB_STEP_SUMMARY
|
|
308
328
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
309
329
|
echo "### Ruby Versions" >> $GITHUB_STEP_SUMMARY
|
|
310
|
-
echo "- Ruby 3.
|
|
330
|
+
echo "- Ruby 3.2, 3.3, 3.4, 4.0" >> $GITHUB_STEP_SUMMARY
|
|
311
331
|
echo "" >> $GITHUB_STEP_SUMMARY
|
|
312
332
|
echo "### Serialization Libraries" >> $GITHUB_STEP_SUMMARY
|
|
313
|
-
echo "- **XML**: REXML, Ox, Nokogiri, Oga, LibXML" >> $GITHUB_STEP_SUMMARY
|
|
333
|
+
echo "- **XML**: REXML, Ox, Nokogiri, Oga, LibXML, Leptris" >> $GITHUB_STEP_SUMMARY
|
|
314
334
|
echo "- **JSON**: JSON, Oj, RapidJSON, YAJL" >> $GITHUB_STEP_SUMMARY
|
|
315
335
|
echo "- **YAML**: Psych" >> $GITHUB_STEP_SUMMARY
|
|
316
336
|
echo "- **TOML**: TOML-RB, Tomlib, tomlrb" >> $GITHUB_STEP_SUMMARY
|
data/Gemfile
CHANGED
|
@@ -12,6 +12,8 @@ unless Gem.win_platform? && RUBY_PLATFORM.include?('aarch64')
|
|
|
12
12
|
gem 'libxml-ruby'
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
gem 'leptris' # Needs the libleptris shared library (LEPTRIS_LIB_PATH or system install)
|
|
16
|
+
gem 'benchmark' # Removed from stdlib in Ruby 4.0
|
|
15
17
|
gem 'base64' # Required for Ruby 3.4+
|
|
16
18
|
gem 'lutaml-model', '~> 0.7'
|
|
17
19
|
gem 'octokit'
|
data/README.adoc
CHANGED
|
@@ -41,6 +41,11 @@ Serialbench is a comprehensive benchmarking suite that evaluates the performance
|
|
|
41
41
|
| v3.4
|
|
42
42
|
| Pure Ruby XML parser with XPath support
|
|
43
43
|
|
|
44
|
+
| XML
|
|
45
|
+
| https://github.com/leptris/leptris-ruby[Leptris]
|
|
46
|
+
| v1.1.0
|
|
47
|
+
| FFI binding to libleptris (pure-C XML 1.0, XPath 1.0, SAX)
|
|
48
|
+
|
|
44
49
|
| XML
|
|
45
50
|
| https://github.com/ruby/rexml[REXML]
|
|
46
51
|
| v3.4.1
|
|
@@ -213,6 +213,46 @@ module Serialbench
|
|
|
213
213
|
exit 1
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
+
desc 'export-data RESULTSET_PATH OUTPUT_JSON', 'Export a resultset as the site dashboard JSON payload'
|
|
217
|
+
long_desc <<~DESC
|
|
218
|
+
Write the aggregated dashboard payload (combined_results, environments,
|
|
219
|
+
metadata) for a resultset as JSON. This is the data contract consumed
|
|
220
|
+
by the Astro site in site/.
|
|
221
|
+
|
|
222
|
+
Optionally also export the raw per-environment YAML files and the
|
|
223
|
+
complete resultset YAML for download links.
|
|
224
|
+
|
|
225
|
+
Examples:
|
|
226
|
+
serialbench resultset export-data results/sets/weekly site/src/data/sample.json --raw-data-dir site/public/data
|
|
227
|
+
DESC
|
|
228
|
+
method_option 'raw-data-dir', type: :string,
|
|
229
|
+
desc: 'Also write per-environment YAML and resultset.yaml to this directory'
|
|
230
|
+
def export_data(resultset_path, output_json)
|
|
231
|
+
resultset = Serialbench::Models::ResultSet.load(resultset_path)
|
|
232
|
+
|
|
233
|
+
if resultset.results.empty?
|
|
234
|
+
say "ResultSet '#{resultset_path}' contains no runs", :yellow
|
|
235
|
+
say "Use 'serialbench resultset add-result' to add runs first", :white
|
|
236
|
+
exit 1
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
payload = Serialbench::SiteGenerator.resultset_payload(resultset)
|
|
240
|
+
output_dir = File.dirname(File.expand_path(output_json))
|
|
241
|
+
FileUtils.mkdir_p(output_dir)
|
|
242
|
+
File.write(output_json, JSON.pretty_generate(payload))
|
|
243
|
+
|
|
244
|
+
say "✅ Dashboard payload exported: #{output_json}", :green
|
|
245
|
+
say "Environments: #{payload['environments'].size}", :cyan
|
|
246
|
+
|
|
247
|
+
if options['raw-data-dir']
|
|
248
|
+
Serialbench::SiteGenerator.export_raw_data(resultset, options['raw-data-dir'])
|
|
249
|
+
say "📦 Raw YAML exported to: #{options['raw-data-dir']}", :cyan
|
|
250
|
+
end
|
|
251
|
+
rescue StandardError => e
|
|
252
|
+
say "Error exporting data: #{e.message}", :red
|
|
253
|
+
exit 1
|
|
254
|
+
end
|
|
255
|
+
|
|
216
256
|
desc 'list', 'List all available resultsets'
|
|
217
257
|
long_desc <<~DESC
|
|
218
258
|
List all resultsets in the results/sets/ directory.
|
|
@@ -15,8 +15,11 @@ module Serialbench
|
|
|
15
15
|
class Platform < Lutaml::Model::Serializable
|
|
16
16
|
attribute :platform_string, :string
|
|
17
17
|
attribute :kind, :string, default: -> { 'local' }
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
# Fully qualified: lutaml-model 0.8+ instance-evals default
|
|
19
|
+
# lambdas, so bare detect_os/detect_arch (class methods) no
|
|
20
|
+
# longer resolve there.
|
|
21
|
+
attribute :os, :string, default: -> { Serialbench::Models::Platform.detect_os }
|
|
22
|
+
attribute :arch, :string, default: -> { Serialbench::Models::Platform.detect_arch }
|
|
20
23
|
attribute :ruby_version, :string, default: -> { RUBY_VERSION }
|
|
21
24
|
attribute :ruby_platform, :string, default: -> { RUBY_PLATFORM }
|
|
22
25
|
attribute :ruby_build_tag, :string
|
|
@@ -29,10 +29,15 @@ module Serialbench
|
|
|
29
29
|
xpath: supports_xpath?,
|
|
30
30
|
namespaces: supports_namespaces?,
|
|
31
31
|
validation: supports_validation?,
|
|
32
|
-
streaming: supports_streaming
|
|
32
|
+
streaming: supports_streaming?,
|
|
33
|
+
stax: supports_stax?
|
|
33
34
|
}
|
|
34
35
|
end
|
|
35
36
|
|
|
37
|
+
def supports_stax?
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
36
41
|
def supports_generation?
|
|
37
42
|
true
|
|
38
43
|
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base_xml_serializer'
|
|
4
|
+
|
|
5
|
+
module Serialbench
|
|
6
|
+
module Serializers
|
|
7
|
+
module Xml
|
|
8
|
+
class LeptrisSerializer < BaseXmlSerializer
|
|
9
|
+
def name
|
|
10
|
+
'leptris'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def parse(xml_string)
|
|
14
|
+
require 'leptris'
|
|
15
|
+
Leptris::XML.parse(xml_string)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def generate(data)
|
|
19
|
+
require 'leptris'
|
|
20
|
+
if data.is_a?(Leptris::XML::Document) || data.is_a?(Leptris::XML::Node)
|
|
21
|
+
data.to_xml(indent: 2)
|
|
22
|
+
else
|
|
23
|
+
build_document_from_data(data).to_xml(indent: 2)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def parse_streaming(xml_string, &block)
|
|
28
|
+
require 'leptris'
|
|
29
|
+
|
|
30
|
+
handler = StreamingHandler.new(&block)
|
|
31
|
+
parser = Leptris::XML::SAX::Parser.new(handler)
|
|
32
|
+
parser.parse_memory(xml_string)
|
|
33
|
+
handler.elements_processed
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def supports_streaming?
|
|
37
|
+
true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def supports_xpath?
|
|
41
|
+
true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# leptris-ruby >= 1.6 exposes Leptris::XML::Pull, a StAX-style
|
|
45
|
+
# cursor (one event per #next). Declared by gem version so the
|
|
46
|
+
# check never triggers the FFI library load.
|
|
47
|
+
def supports_stax?
|
|
48
|
+
spec = Gem.loaded_specs['leptris'] || Gem::Specification.find_by_name('leptris')
|
|
49
|
+
!spec.nil? && spec.version >= Gem::Version.new('1.6.0')
|
|
50
|
+
rescue Gem::LoadError
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def version
|
|
55
|
+
return 'unknown' unless available?
|
|
56
|
+
|
|
57
|
+
require 'leptris'
|
|
58
|
+
Leptris::VERSION
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def library_require_name
|
|
62
|
+
'leptris'
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The gem's require succeeds even when the libtaurus shared library is
|
|
66
|
+
# missing -- the FFI load happens lazily at first use. Probe with a
|
|
67
|
+
# real parse so "available" means "actually usable".
|
|
68
|
+
def available?
|
|
69
|
+
return @available if defined?(@available)
|
|
70
|
+
|
|
71
|
+
@available = begin
|
|
72
|
+
require 'leptris'
|
|
73
|
+
Leptris::XML.parse('<probe/>')
|
|
74
|
+
true
|
|
75
|
+
rescue StandardError, LoadError
|
|
76
|
+
false
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def build_document_from_data(data, root_name = 'root')
|
|
83
|
+
document = Leptris::XML.parse('<root/>')
|
|
84
|
+
document.root.name = sanitize_element_name(root_name)
|
|
85
|
+
append_data(document.root, data)
|
|
86
|
+
document
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def append_data(element, data)
|
|
90
|
+
case data
|
|
91
|
+
when Hash
|
|
92
|
+
data.each do |key, value|
|
|
93
|
+
child = element.document.create_element(sanitize_element_name(key.to_s))
|
|
94
|
+
element.add_child(child)
|
|
95
|
+
append_data(child, value)
|
|
96
|
+
end
|
|
97
|
+
when Array
|
|
98
|
+
data.each do |item|
|
|
99
|
+
child = element.document.create_element('item')
|
|
100
|
+
element.add_child(child)
|
|
101
|
+
append_data(child, item)
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
element.content = data.to_s
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def sanitize_element_name(name)
|
|
109
|
+
sanitized = name.to_s.gsub(/[^a-zA-Z0-9_]/, '_')
|
|
110
|
+
sanitized = "element_#{sanitized}" if sanitized.empty? || sanitized =~ /\A\d/
|
|
111
|
+
sanitized
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# SAX handler counting elements, mirroring the Nokogiri adapter's
|
|
115
|
+
# StreamingHandler shape (Leptris SAX delivers attrs as [name, value]
|
|
116
|
+
# pairs, same as Nokogiri::XML::SAX). Plain duck-typed class --
|
|
117
|
+
# Leptris::XML::SAX::Parser needs no base class, and subclassing it
|
|
118
|
+
# would trigger the FFI library load at file-load time.
|
|
119
|
+
class StreamingHandler
|
|
120
|
+
attr_reader :elements_processed
|
|
121
|
+
|
|
122
|
+
def initialize(&block)
|
|
123
|
+
@block = block
|
|
124
|
+
@elements_processed = 0
|
|
125
|
+
@element_stack = []
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# No-op callbacks the parser invokes unconditionally (the base
|
|
129
|
+
# Leptris::XML::SAX::Document normally provides these).
|
|
130
|
+
def start_document; end
|
|
131
|
+
def end_document; end
|
|
132
|
+
def xmldecl(_version, _encoding, _standalone); end
|
|
133
|
+
def comment(_string); end
|
|
134
|
+
def processing_instruction(_name, _content); end
|
|
135
|
+
def start_prefix_mapping(_prefix, _uri); end
|
|
136
|
+
def end_prefix_mapping(_prefix); end
|
|
137
|
+
def warning(_string); end
|
|
138
|
+
def error(_message, _line = 0, _column = 0); end
|
|
139
|
+
|
|
140
|
+
def start_element(name, attrs = [])
|
|
141
|
+
@elements_processed += 1
|
|
142
|
+
@element_stack.push({ name: name, attributes: Hash[attrs], children: [], text: '' })
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def end_element(_name)
|
|
146
|
+
element = @element_stack.pop
|
|
147
|
+
if @element_stack.empty?
|
|
148
|
+
@block&.call(element)
|
|
149
|
+
else
|
|
150
|
+
@element_stack.last[:children] << element
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def characters(string)
|
|
155
|
+
return if string.strip.empty?
|
|
156
|
+
|
|
157
|
+
@element_stack.last[:text] += string if @element_stack.any?
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def cdata_block(string)
|
|
161
|
+
@element_stack.last[:text] += string if @element_stack.any?
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -10,6 +10,7 @@ require_relative 'serializers/xml/ox_serializer'
|
|
|
10
10
|
require_relative 'serializers/xml/nokogiri_serializer'
|
|
11
11
|
require_relative 'serializers/xml/oga_serializer'
|
|
12
12
|
require_relative 'serializers/xml/libxml_serializer'
|
|
13
|
+
require_relative 'serializers/xml/leptris_serializer'
|
|
13
14
|
|
|
14
15
|
# JSON Serializers
|
|
15
16
|
require_relative 'serializers/json/base_json_serializer'
|
|
@@ -38,7 +39,8 @@ module Serialbench
|
|
|
38
39
|
Xml::OxSerializer,
|
|
39
40
|
Xml::NokogiriSerializer,
|
|
40
41
|
Xml::OgaSerializer,
|
|
41
|
-
Xml::LibxmlSerializer
|
|
42
|
+
Xml::LibxmlSerializer,
|
|
43
|
+
Xml::LeptrisSerializer
|
|
42
44
|
],
|
|
43
45
|
json: [
|
|
44
46
|
Json::JsonSerializer,
|