jmespath-ruby 0.1.0 → 0.1.2

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: cf530e6a526f4e83cc7fd29f7f527fedf0b28add8d0e57b362db7ac96b379cb7
4
- data.tar.gz: 29b4682722f6371663cd0c84f29119db2c1fd89943ad60db395aead75adc2456
3
+ metadata.gz: f9f2db0a8d51af9e3258964210daf3aa1391f97370fca0d0e5610045aad9e012
4
+ data.tar.gz: ec560bc1d5607e14175267d6b1bb82329a3d126686d99393c2cd6a8e7bc62585
5
5
  SHA512:
6
- metadata.gz: 40f6a36ceb35cb72fe6dd62651f51ae41a8b79f204f71b6941c754f93ac5a2dd18e08ef9f2ee98791073bae4493b636ebd79ef8196541cccd3b92853310faa22
7
- data.tar.gz: 8a4e7dfa8ea63e0e6c5e073f7e893a34d76de3267a6300ba5e60cd27d51cc9566be26985dd7e56b51bf31b7b5160bd2e41e634c9f8358483e358631a43357424
6
+ metadata.gz: b8b8a4b5d663ae5b6ac54d093867c94ea420d836d44f1de5de1b781c21a984277a8211f7a6343392b335d0e451b7f3f749802e0ad910fc0af45d573fb305d2b9
7
+ data.tar.gz: 985c5eddf2e47265eaa74f37afd4ea73094ae84f6a87ec90ec0c0501be1af03b3c46a4d95207be57d42ec47f4b94dc2b632a1e87b5eabfa712e3ba4232753794
data/CHANGELOG.md CHANGED
@@ -1,9 +1,37 @@
1
1
  # Changelog
2
2
 
3
- ## 0.1.0 - 2026-11-01
3
+ All notable changes are recorded here using [Keep a Changelog 2.0.0](https://keepachangelog.com/en/2.0.0/).
4
+ Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html) for the public contract defined in [VERSIONING.md](VERSIONING.md).
5
+
6
+ ## [Unreleased]
7
+
8
+ ## [0.1.2] - 2026-11-02
9
+
10
+ ### Added
11
+
12
+ - Add `JMESPath::Diagnostic.format` for syntax reasons, source coordinates, query excerpts, and carets.
13
+ - Expose immutable function validation data, including argument positions, expected and actual types, arity, and query byte ranges.
14
+ - Add `Functions#names`, `Functions#metadata`, and optional custom function descriptions for immutable registry inspection.
15
+ - Suggest similar names from the active registry for unknown functions; expose the missing name and ranked suggestions on the error.
16
+
17
+ ### Changed
18
+
19
+ - Show located CLI syntax, function type, and arity diagnostics; describe validation and suggestions in CLI help.
20
+ - Report malformed CLI input as `Invalid JSON input` without exposing JSON parser messages or document fragments.
21
+
22
+ ## [0.1.1] - 2026-11-01
23
+
24
+ ### Changed
25
+
26
+ - Expand installation, support, contribution, and performance comparison documentation.
27
+ - Publish gems with build provenance, a dependency inventory, and attested release assets.
28
+
29
+ ## [0.1.0] - 2026-11-01
4
30
 
5
31
  Initial implementation for Ruby 3.3 and later.
6
32
 
33
+ ### Added
34
+
7
35
  - Implement the JMESPath language and all 26 standard functions.
8
36
  - Preserve the public `require "jmespath"` and `JMESPath.search` entry points under the `jmespath-ruby` package name.
9
37
  - Add immutable compiled expressions, bounded synchronized caching, and immutable custom function registries.
@@ -16,3 +44,8 @@ Initial implementation for Ruby 3.3 and later.
16
44
 
17
45
  The compatibility guide documents intentional corrections and limits.
18
46
  Applications that depend on the gem name `jmespath` must change that dependency explicitly.
47
+
48
+ [unreleased]: https://github.com/svyatov/jmespath-ruby/compare/v0.1.2...HEAD
49
+ [0.1.2]: https://github.com/svyatov/jmespath-ruby/compare/v0.1.1...v0.1.2
50
+ [0.1.1]: https://github.com/svyatov/jmespath-ruby/compare/v0.1.0...v0.1.1
51
+ [0.1.0]: https://github.com/svyatov/jmespath-ruby/releases/tag/v0.1.0
data/CONTRIBUTING.md CHANGED
@@ -3,6 +3,25 @@
3
3
  Use Ruby 3.3 or later.
4
4
  Install the locked development bundle with `bundle install`.
5
5
 
6
+ Outside contributions are welcome. This guide is the project's contribution standard.
7
+
8
+ ## Submit a change
9
+
10
+ Fork the repository and create a branch from `main`.
11
+ Use a branch name such as `fix/query-validation` and Conventional Commits for commit messages.
12
+ Keep each pull request focused on one change.
13
+ Open a pull request against `main` describing the problem, the change, and any public API consequences.
14
+
15
+ New functionality must include tests. Bug fixes must include a regression test that fails before the fix.
16
+ Follow the checks, fixture rules, and documentation requirements in this guide.
17
+ Follow [VERSIONING.md](VERSIONING.md) when changing the public contract.
18
+ Participants must follow the [code of conduct](CODE_OF_CONDUCT.md).
19
+
20
+ ## Decisions and continuity
21
+
22
+ Leonid Svyatov is the sole maintainer and decides which changes to accept, merge, and release.
23
+ No successor is arranged if he stops maintaining the project.
24
+
6
25
  ## Checks
7
26
 
8
27
  ```sh
@@ -16,6 +35,8 @@ Coverage requires 100% lines and branches.
16
35
  Every measurable library method must score at most 10 in coverage-aware CRAP analysis.
17
36
  A missing coverage report fails the check.
18
37
  Unmeasurable methods are reported for review.
38
+ CI reports existing dependency advisories without blocking unrelated changes.
39
+ Dependency review blocks pull requests that introduce known vulnerabilities.
19
40
 
20
41
  Run `bundle exec rspec spec/unit/parser_spec.rb` for a focused test cycle.
21
42
  Focused RSpec runs collect coverage without enforcing full-suite thresholds.
@@ -47,11 +68,13 @@ Publishing and remote repository operations require separate authorization.
47
68
  Update `lib/jmespath/version.rb` and `CHANGELOG.md` together.
48
69
  Run `bundle exec rake release:check` before creating a release tag.
49
70
  Push a `vVERSION` tag that exactly matches `JMESPath::VERSION`.
50
- The release workflow runs the full quality matrix, verifies the tag, archives the built gem, and publishes through RubyGems trusted publishing.
71
+ The release workflow runs the quality matrix and builds the gem, dependency inventory, checksums, and changelog-derived release notes.
72
+ It verifies the tag before publication and publishes the exact archived gem through RubyGems trusted publishing after manual approval.
73
+ It uploads provenance to RubyGems and attaches the attested gem, inventory, and checksums to the GitHub release.
51
74
 
52
75
  The trusted publisher is restricted to `svyatov/jmespath-ruby`, workflow `release.yml`, and GitHub environment `release`.
53
76
  The environment accepts release tags matching `v*`.
54
77
  The token exchange declares the account's immutable RubyGems owner ID, `71752`, which is required for the first publication.
55
78
  No long-lived RubyGems API key is required.
56
- After publication, create the GitHub release from the archived gem and its matching changelog entry.
57
- If only GitHub release creation fails, reuse that artifact; do not republish the gem.
79
+ Use manual workflow dispatch on a branch to verify and build without publishing.
80
+ If only GitHub release creation fails, rerun the failed job to reuse the archived artifact; do not republish the gem.
data/README.md CHANGED
@@ -1,43 +1,101 @@
1
1
  # jmespath-ruby
2
2
 
3
- A pure Ruby implementation of JMESPath for Ruby 3.3 and later.
3
+ Search JSON and Ruby data with reusable queries and typed custom functions.
4
4
 
5
- Search JSON-compatible values, compile expressions for reuse, and register typed custom functions.
6
- The package keeps `require "jmespath"` and `JMESPath.search` while using the gem name `jmespath-ruby`.
5
+ - Runs on Ruby 3.3 and later with a pure Ruby implementation.
6
+ - Replaces [jmespath.rb](https://github.com/jmespath/jmespath.rb)'s documented `JMESPath.search` API; see the [migration limits](#replace-jmespathrb).
7
+ - Reuses compiled queries and supports typed custom functions.
8
+ - Measured 7.97x geometric-mean public-search speedup over jmespath 1.6.2 on MRI 4.0.9; see the [benchmark results](docs/guides/comparison.md).
7
9
 
8
10
  ## Install
9
11
 
10
- Add the gem to your Gemfile:
12
+ In your Ruby project, add the gem with Bundler. Run `bundle init` first if the project has no Gemfile.
11
13
 
12
- ```ruby
13
- gem "jmespath-ruby", "~> 0.1.0"
14
+ ```sh
15
+ bundle add jmespath-ruby --version "~> 0.1.2"
14
16
  ```
15
17
 
16
- Or install it directly:
18
+ Save this Ruby example as `example.rb`:
17
19
 
18
- ```sh
19
- gem install jmespath-ruby
20
+ ```ruby
21
+ require "jmespath"
22
+
23
+ data = {user: {name: "Ada"}}
24
+ JMESPath.search("user.name", data) #=> "Ada"
20
25
  ```
21
26
 
22
- ### Install from this checkout
27
+ Run it with `bundle exec ruby example.rb`. The final expression returns `"Ada"`.
23
28
 
24
- ```sh
25
- bundle install
26
- bundle exec rake build
27
- gem install ./pkg/jmespath-ruby-0.1.0.gem
29
+ ## Performance
30
+
31
+ **Fastest public search in every matching MRI benchmark case.**
32
+ On MRI 4.0.9, jmespath-ruby beats each competing implementation on every workload that both return correctly.
33
+
34
+ | Implementation | Version | Correct workloads | jmespath-ruby public-search speedup |
35
+ | --- | --- | ---: | ---: |
36
+ | **jmespath-ruby** | **0.1.0** | **18/18** | **Baseline** |
37
+ | jmespath (jmespath.rb) | 1.6.2 | 18/18 | **7.97x** |
38
+ | BurtPath | 1.1.2 | 15/18 | **9.99x** |
39
+ | Jamespath | 0.5.1 | 13/18 | **8.07x** |
40
+
41
+ Speedups use geometric means of median times across each competitor's correctly evaluated workloads.
42
+ Measurements use five rounds of 20,000 searches on an Apple M1 Pro.
43
+ The suite includes all 12 upstream benchmark cases and six supplemental cases.
44
+ Results vary by workload: **1.35x to 78.48x faster than jmespath 1.6.2** in this run.
45
+
46
+ Query reuse has different results: BurtPath's private cached runtime is faster in five workloads, and Jamespath has three lower medians.
47
+ The [full comparison](docs/guides/comparison.md) includes those results, JRuby, failures, methodology, and raw samples.
48
+ The JRuby-only `jmespath-jruby` 1.0.0.pre0 fails on the tested JRuby 10.1 runtime; its results are recorded separately.
49
+
50
+ ## Why choose jmespath-ruby?
51
+
52
+ - **Reuse work across searches.** Compile once, save query options, and share immutable expressions across threads.
53
+ - **Add functions without global patches.** Typed, immutable registries validate arguments before your callback runs.
54
+ - **Type-check integrations.** The gem ships RBS signatures for its public API.
55
+ - **Use Ruby data directly.** Search string-keyed or symbol-keyed hashes, Structs, arrays, JSON streams, and Pathname inputs.
56
+ - **Control query growth.** The synchronized cache holds at most 32 queries and 1 MiB of source text; expression nesting has an explicit limit.
57
+
58
+ Both Ruby and Python upstream compliance corpora run in the test suite, alongside Ruby-specific input and error checks.
59
+ The [comparison with existing implementations](docs/guides/comparison.md) includes exact versions, rejected workloads, and reproducible measurements.
60
+
61
+ | Feature | jmespath-ruby | jmespath | BurtPath | Jamespath | jmespath-jruby |
62
+ | --- | --- | --- | --- | --- | --- |
63
+ | Public query compilation | Yes | No | No | Yes | Yes |
64
+ | Documented typed function registry | Yes | No | No | No | No |
65
+ | Packaged RBS signatures | Yes | No | No | No | No |
66
+ | Pure Ruby implementation | Yes | Yes | Yes | Yes | No |
67
+ | Ordinary `JMESPath.search` calls | Yes | Yes | Yes | No | No |
68
+
69
+ `jmespath` and BurtPath support reuse through private runtime classes.
70
+ The [full comparison](docs/guides/comparison.md) distinguishes those classes from public compilation APIs.
71
+
72
+ ## Replace jmespath.rb
73
+
74
+ The official jmespath.rb project publishes the gem named `jmespath`.
75
+ Replace that Gemfile entry:
76
+
77
+ ```diff
78
+ -gem "jmespath"
79
+ +gem "jmespath-ruby", "~> 0.1.2"
28
80
  ```
29
81
 
30
- For a local Bundler application:
82
+ Run `bundle install`. Your Ruby code keeps the same entry points:
31
83
 
32
84
  ```ruby
33
- gem "jmespath-ruby", path: "/path/to/jmespath-ruby"
85
+ require "jmespath"
86
+ JMESPath.search("user.name", {user: {name: "Ada"}}) #=> "Ada"
34
87
  ```
35
88
 
36
89
  Use one provider per bundle.
37
- A dependency on the package named `jmespath` needs its own dependency change; this gem cannot satisfy that name automatically.
90
+ A third-party dependency that requires the package named `jmespath` needs a dependency change; Bundler cannot substitute `jmespath-ruby` automatically.
91
+ Private parser/runtime classes and exact error messages are outside the replacement contract.
92
+ Validation is stricter for invalid function arguments, and empty `min_by`/`max_by` results return nil.
93
+ See [compatibility and deliberate corrections](docs/guides/compatibility.md) before migrating.
38
94
 
39
95
  ## Search
40
96
 
97
+ Use `JMESPath.search` in Ruby code to evaluate a query:
98
+
41
99
  ```ruby
42
100
  require "jmespath"
43
101
 
@@ -53,6 +111,8 @@ The library preserves selected input objects and does not mutate the input.
53
111
 
54
112
  ## Compile once
55
113
 
114
+ Use `JMESPath.compile` in Ruby code to reuse a parsed expression:
115
+
56
116
  ```ruby
57
117
  expression = JMESPath.compile("user.name")
58
118
  expression.search({user: {name: "Ada"}}) #=> "Ada"
@@ -63,7 +123,7 @@ Compiled expressions are immutable and safe to share between threads.
63
123
  Each search uses separate evaluation state.
64
124
  Custom callbacks and input objects remain the caller's responsibility.
65
125
 
66
- Save evaluation options when compiling a query, then use `search` or its `call` alias:
126
+ In Ruby code, save evaluation options when compiling a query, then use `search` or its `call` alias:
67
127
 
68
128
  ```ruby
69
129
  query = JMESPath.compile("length(name)", disable_visit_errors: true)
@@ -73,6 +133,8 @@ query.call({name: nil}) #=> nil
73
133
 
74
134
  ## Custom functions
75
135
 
136
+ Create a function registry in Ruby code and pass it to `JMESPath.search`:
137
+
76
138
  ```ruby
77
139
  functions = JMESPath::Functions.default.with("double", signature: [[:number]]) { |number| number * 2 }
78
140
  JMESPath.search("double(price)", {price: 12}, functions: functions) #=> 24
@@ -82,6 +144,25 @@ Registration returns a separate immutable registry.
82
144
  An existing function requires `override: true` to replace it.
83
145
  Every argument, including variadic arguments, is validated before the callback runs.
84
146
 
147
+ Inspect registered functions in Ruby without executing their callbacks:
148
+
149
+ ```ruby
150
+ JMESPath::Functions.default.names.include?("length") #=> true
151
+ metadata = JMESPath::Functions.default.metadata("length")
152
+ metadata.signature #=> [[:string, :array, :object]]
153
+ metadata.description #=> "Character or element count."
154
+ ```
155
+
156
+ Use `description:` when registering a custom function to include its description in immutable metadata.
157
+ See [registry inspection](docs/guides/functions.md#registry-inspection) for all fields and override behavior.
158
+
159
+ ## Errors and diagnostics
160
+
161
+ Format syntax failures in Ruby with `JMESPath::Diagnostic.format(error)` to get source coordinates, a query excerpt, and a caret.
162
+ Function type and arity errors expose immutable validation data, including the function name, signature, argument counts or types, and available source ranges.
163
+ Unknown-function errors expose the missing name and up to three suggestions from the active registry.
164
+ See [errors and diagnostics](docs/guides/errors.md) for examples, nullable fields, and source-location limits.
165
+
85
166
  ## Command line
86
167
 
87
168
  Both `jmespath` and `jmespath.rb` accept an expression and a JSON document on standard input.
@@ -91,7 +172,12 @@ printf '%s\n' '{"user":{"name":"Ada"}}' | jmespath 'user.name'
91
172
  ```
92
173
 
93
174
  The result goes to standard output as JSON.
94
- Errors go to standard error with a nonzero status.
175
+ Syntax, function type, and arity failures include source coordinates, a query excerpt, and a caret when available.
176
+ Unknown functions suggest similar registered names.
177
+ Malformed JSON input reports `Invalid JSON input` without including document fragments.
178
+ These failures go to standard error and exit with status 1.
179
+ Usage failures exit with status 2; `--help` and `--version` exit with status 0 without reading input.
180
+ See [CLI diagnostics](docs/guides/errors.md#cli-diagnostics) for examples and fallback behavior.
95
181
 
96
182
  ## Guides
97
183
 
@@ -101,7 +187,19 @@ Errors go to standard error with a nonzero status.
101
187
  - [Errors and limits](docs/guides/errors.md)
102
188
  - [Compatibility and migration](docs/guides/compatibility.md)
103
189
  - [Performance measurements](docs/guides/performance.md)
190
+ - [Comparison with other implementations](docs/guides/comparison.md)
104
191
  - [Development](CONTRIBUTING.md)
192
+ - [Changelog](CHANGELOG.md)
193
+
194
+ ## Help and maintenance
195
+
196
+ This project is maintained by Leonid Svyatov.
197
+ Use [GitHub issues](https://github.com/svyatov/jmespath-ruby/issues) for questions and defect reports.
198
+ Report vulnerabilities through the private channel in [SECURITY.md](SECURITY.md).
199
+
200
+ Release assets include the gem, `sbom.cdx.json`, and `SHA256SUMS`.
201
+ Verify a downloaded asset with `gh attestation verify ASSET --repo svyatov/jmespath-ruby --signer-workflow svyatov/jmespath-ruby/.github/workflows/release.yml`.
202
+ Check downloaded files against the manifest with `sha256sum -c SHA256SUMS`.
105
203
 
106
204
  ## Verify and document
107
205
 
data/SECURITY.md CHANGED
@@ -11,6 +11,7 @@ Bound request sizes and runtime resources at the application boundary.
11
11
  Run `bundle exec rake audit` to refresh the dependency advisory database and check the lockfile.
12
12
  A failed database update fails the audit.
13
13
 
14
- Report security concerns through a private maintainer contact channel.
14
+ Report vulnerabilities privately to [leonid@svyatov.com](mailto:leonid@svyatov.com).
15
+ Leonid Svyatov will respond within 7 days.
16
+ Include the affected version, a minimal reproduction, and the security impact.
15
17
  Do not post credentials or private application data in public reports.
16
- Private repository reporting must be configured before publication.
@@ -0,0 +1,189 @@
1
+ # Ruby implementation comparison
2
+
3
+ The comparison covers the latest published versions found on November 1, 2026.
4
+ [RubyGems search](https://rubygems.org/api/v1/search.json?query=jmespath) and the upstream repositories identify these implementations:
5
+
6
+ | Gem | Version | Upstream | API used |
7
+ | --- | --- | --- | --- |
8
+ | jmespath-ruby | 0.1.0 checkout | [svyatov/jmespath-ruby](https://github.com/svyatov/jmespath-ruby) | `JMESPath.search`, `JMESPath.compile` |
9
+ | jmespath | 1.6.2 | [jmespath/jmespath.rb](https://github.com/jmespath/jmespath.rb) | `JMESPath.search`, persistent `JMESPath::Runtime` |
10
+ | burtpath | 1.1.2 | [burtcorp/burtpath](https://github.com/burtcorp/burtpath) | `JMESPath.search`, persistent `JMESPath::Runtime` |
11
+ | jamespath | 0.5.1 | [lsegal/jamespath](https://github.com/lsegal/jamespath) | `Jamespath.search`, `Jamespath.compile` |
12
+ | jmespath-jruby | 1.0.0.pre0 | [burtcorp/jmespath-jruby](https://github.com/burtcorp/jmespath-jruby) | `JmesPath#compile`, compiled query reuse |
13
+
14
+ The gem named `jmespath.rb` is an empty name-protection package, not the official implementation.
15
+ The official implementation uses the gem name `jmespath`.
16
+ `stack_master_json_parameter_store` consumes JMESPath rather than implementing it.
17
+ The mruby project has no RubyGems release in this search and targets a different Ruby runtime.
18
+
19
+ Package checksums and exclusions are recorded in the [source manifest](../../benchmark/results/comparison-sources.json).
20
+ This project's library source is revision `609c178dfe3be49e13c64ae1b49790570c9fec37`; each report also fingerprints the measured library files.
21
+
22
+ ## MRI results
23
+
24
+ The following results use MRI 4.0.9 without YJIT.
25
+ Each speedup is the geometric mean of competitor time divided by this library's time, using only workloads both implementations evaluate correctly.
26
+ Each workload's time is the median of its five samples before ratios are calculated.
27
+ Each workload contributes equally to the mean.
28
+ A value above 1 favors this library; a value below 1 favors the competitor.
29
+
30
+ | Implementation | Correct search workloads | Public-search speedup | Search speedup range | Reuse speedup |
31
+ | --- | ---: | ---: | ---: | ---: |
32
+ | **jmespath-ruby 0.1.0** | **✅ 18/18** | **1.00x** | **Baseline** | **1.00x** |
33
+ | jmespath 1.6.2 | ✅ 18/18 | 7.97x | 1.35x to 78.48x | 4.35x |
34
+ | BurtPath 1.1.2 | ❌ 15/18 | 9.99x | 1.74x to 80.59x | 1.04x |
35
+ | Jamespath 0.5.1 | ❌ 13/18 | 8.07x | 2.74x to 26.91x | 1.38x |
36
+
37
+ Public search is faster in every mutually passing workload.
38
+ Reuse is not uniformly faster: BurtPath has lower medians in five of 15 shared workloads; Jamespath has lower medians in three of 13.
39
+ The 1.04x aggregate against BurtPath represents near parity, not a decisive reuse advantage.
40
+ No statistical significance claim is made for close medians.
41
+
42
+ BurtPath's faster reuse cases are `single_expression`, `deep_nesting_50_index`, `multi_wildcard_field`, `wildcard_with_field_match`, and `wildcard_with_field_match2`.
43
+ Jamespath's lower reuse medians occur in `single_dot_expression`, `double_dot_expression`, and `dot_no_match`.
44
+
45
+ Raw results: [all MRI providers](../../benchmark/results/comparison-mri-4.0.9.json) and [Jamespath follow-up](../../benchmark/results/comparison-jamespath-mri-4.0.9.json).
46
+ The follow-up removes a per-call adapter conditional; its Jamespath measurements supply the tables above.
47
+ The original full run remains available, including every failed case and sample.
48
+
49
+ ## JRuby results
50
+
51
+ These measurements use JRuby 10.1.0.0 and Temurin Java 21.0.11.
52
+ They use the same median and geometric-mean calculation as the MRI table, with separate timing samples.
53
+
54
+ | Implementation | Correct public-search workloads | Correct reuse workloads | Public-search speedup | Reuse speedup |
55
+ | --- | ---: | ---: | ---: | ---: |
56
+ | **jmespath-ruby 0.1.0** | **✅ 18/18** | **✅ 18/18** | **1.00x** | **1.00x** |
57
+ | jmespath 1.6.2 | ✅ 18/18 | ✅ 18/18 | 7.80x | 5.24x |
58
+ | BurtPath 1.1.2 | ❌ 15/18 | ❌ 16/18 | 10.45x | 0.88x |
59
+ | Jamespath 0.5.1 | ❌ 13/18 | ❌ 13/18 | 10.23x | 0.97x |
60
+ | jmespath-jruby 1.0.0.pre0 | ❌ 0/18 | ❌ 0/18 | Unavailable | Unavailable |
61
+
62
+ Public search has lower median times in every mutually passing workload.
63
+ The range against `jmespath` is 1.42x to 87.88x.
64
+ Reuse favors BurtPath in 12 of 16 shared workloads, Jamespath in six of 13, and `jmespath` in three of 18.
65
+ This library therefore does not have a universal query-reuse advantage on JRuby.
66
+
67
+ BurtPath's filter fails during public search but passes during reuse in this execution sequence.
68
+ Its other rejected workloads are the stepped slice and Unicode function.
69
+ The Java-backed implementation fails before timing because its JAR calls a removed JRuby runtime method.
70
+ See the [complete JRuby samples](../../benchmark/results/comparison-jruby-10.1.0.0.json), including all errors.
71
+
72
+ ## Beyond speed
73
+
74
+ | Capability | jmespath-ruby | jmespath | BurtPath | Jamespath | jmespath-jruby |
75
+ | --- | --- | --- | --- | --- | --- |
76
+ | Public query compilation | ✅ `compile` | ❌ Private `Runtime` | ❌ Private `Runtime` | ✅ `compile` | ✅ `compile` |
77
+ | Documented typed function registry | ✅ `Functions#with` | ❌ | ❌ | ❌ | ❌ |
78
+ | Packaged RBS signatures | ✅ | ❌ | ❌ | ❌ | ❌ |
79
+ | Pure Ruby implementation | ✅ | ✅ | ✅ | ✅ | ❌ Java extension |
80
+ | Ordinary `JMESPath.search` calls | ✅ | ✅ Original API | ✅ | ❌ Different module | ❌ Instance API |
81
+
82
+ This library's compiled expressions and function registries are immutable.
83
+ Save options at compilation, reuse queries across threads, and extend one registry without changing other callers.
84
+ Callback arguments are validated, including variadic arguments; callback errors retain their original exception and backtrace.
85
+ These guarantees are described in the [compilation](compilation.md) and [function](functions.md) guides.
86
+
87
+ Ruby input compatibility, named errors, and the positional options hash make migration from jmespath.rb practical.
88
+ The [compatibility guide](compatibility.md) lists the limits: package dependencies need updating, private internals differ, and several invalid-input behaviors are corrected.
89
+
90
+ ## Method
91
+
92
+ All providers receive the same JSON-compatible input and independently recorded expected output.
93
+ The suite contains all 12 upstream benchmark cases and six supplemental cases: field lookup, projection, filter, sorting, slicing, and Unicode.
94
+ Each operation must return the expected result before warmup, after warmup, and after timing.
95
+ Rejected syntax, exceptions, and incorrect results appear as failures with no timing.
96
+
97
+ Two modes measure distinct application patterns:
98
+
99
+ - **Public search:** call the documented search API for each document. The Java-backed provider compiles on each call because it exposes no equivalent module method.
100
+ - **Reuse:** compile once or retain the provider's runtime, then query repeatedly. Parsing and setup are outside the measured loop.
101
+
102
+ The retained `JMESPath::Runtime` classes in `jmespath` and BurtPath are marked `@api private`.
103
+ They provide a stronger cached baseline but are not public compilation APIs.
104
+ This library, Jamespath, and the Java-backed implementation expose compilation APIs directly.
105
+
106
+ Measurements run on an Apple M1 Pro with arm64 macOS.
107
+ Each round starts a separate process for each provider.
108
+ Provider order and workload order rotate between rounds.
109
+ Providers run sequentially, using the same interpreter within each report, default garbage collection, and pre-parsed input.
110
+ JSON parsing, process startup, and query construction for reuse are excluded.
111
+ Results describe these workloads and runtime configurations; they do not measure application throughput or cold startup.
112
+
113
+ MRI uses 2,000 warmup calls and 20,000 measured calls in each of five rounds, with YJIT disabled.
114
+ JRuby uses 10,000 warmup calls and 10,000 measured calls in each of five rounds, with Temurin Java 21.0.11.
115
+ Its results are separate because its JVM and JIT differ from MRI.
116
+ JRuby does not provide MRI's allocated-object counter; allocation fields are null there.
117
+ These runs do not measure peak RAM.
118
+
119
+ Published gem contents are loaded directly with `-I`, without Bundler dependency activation.
120
+ This allows a source comparison on the same interpreter and JSON library.
121
+ It does not verify that each published package can be installed in a current application bundle.
122
+ In particular, BurtPath declares `json ~> 1.8`; the MRI source comparison uses JSON 3.1.0.
123
+
124
+ ## Compatibility findings
125
+
126
+ Both this library and `jmespath` produce the expected results for all 18 workloads on the tested interpreters.
127
+ BurtPath fails the filter, stepped slice, and Unicode workloads on MRI.
128
+ Its filter raises a missing-constant error, the slice returns nil, and `reverse` is unknown.
129
+ Jamespath rejects the filter, sorting, stepped slice, Unicode function, and pipe workloads.
130
+ Its 13 remaining workloads return the expected results.
131
+
132
+ The Java-backed gem cannot evaluate these workloads on JRuby 10.1.0.0.
133
+ Its bundled JAR calls the removed `org.jruby.RubyObject.getNativeTypeIndex()` method.
134
+ The runner loads `jruby` before the extension to initialize its bridge; the binary incompatibility remains.
135
+ This is a failure on the tested current runtime, not a claim about historical JRuby releases.
136
+
137
+ ## Reproduce
138
+
139
+ Fetch the release artifacts in a scratch directory, then unpack their sources:
140
+
141
+ ```sh
142
+ gem fetch jmespath --version 1.6.2
143
+ gem unpack jmespath-1.6.2.gem
144
+ gem fetch burtpath --version 1.1.2
145
+ gem unpack burtpath-1.1.2.gem
146
+ gem fetch jamespath --version 0.5.1
147
+ gem unpack jamespath-0.5.1.gem
148
+ gem fetch jmespath-jruby --version 1.0.0.pre0 --platform java
149
+ gem unpack jmespath-jruby-1.0.0.pre0-java.gem
150
+ ```
151
+
152
+ This does not install their declared dependencies.
153
+ Compare the downloaded files' SHA-256 digests with the source manifest.
154
+ Keep source directories separate because several packages provide the same require path and constants.
155
+ Create a JSON configuration with a root, provider adapter, and version for each implementation:
156
+
157
+ ```json
158
+ {
159
+ "jmespath-ruby": {"root": "/path/to/this/checkout", "provider": "jmespath", "version": "0.1.0"},
160
+ "jmespath": {"root": "/path/to/jmespath-1.6.2", "provider": "jmespath", "version": "1.6.2"},
161
+ "burtpath": {"root": "/path/to/burtpath-1.1.2", "provider": "jmespath", "version": "1.1.2"},
162
+ "jamespath": {"root": "/path/to/jamespath-0.5.1", "provider": "jamespath", "version": "0.5.1"}
163
+ }
164
+ ```
165
+
166
+ Run the MRI comparison:
167
+
168
+ ```sh
169
+ ruby script/compare.rb --config /path/to/providers.json --iterations 20000 --warmup 2000 --rounds 5 --output /tmp/comparison.json
170
+ ```
171
+
172
+ For JRuby, add the Java-backed provider to the configuration:
173
+
174
+ ```json
175
+ "jmespath-jruby": {"root": "/path/to/jmespath-jruby-1.0.0.pre0-java", "provider": "jmespath-jruby", "version": "1.0.0.pre0"}
176
+ ```
177
+
178
+ Run the script with the JRuby executable and a compatible `JAVA_HOME`:
179
+
180
+ ```sh
181
+ jruby script/compare.rb --config /path/to/providers.json --iterations 10000 --warmup 10000 --rounds 5 --output /tmp/comparison-jruby.json
182
+ ```
183
+
184
+ Keep JRuby results in a separate output file.
185
+ The runner rejects changes to library sources or workload files during measurement.
186
+ The saved MRI and JRuby reports hash files in their runtime's glob traversal order, producing different fingerprints for identical sources.
187
+ Review verified the file bytes match; the current runner explicitly sorts paths to make future fingerprints portable across runtimes.
188
+ The source manifest records the harness revision needed to reproduce each historical fingerprint.
189
+ For the earlier private-node, cache-churn, startup, and memory experiments, see [performance measurements](performance.md).