fhirpath 0.2.0.pre1
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 +7 -0
- data/.rubocop.yml +73 -0
- data/CHANGELOG.md +77 -0
- data/CONTRIBUTING.md +67 -0
- data/Gemfile +12 -0
- data/LICENSE +21 -0
- data/README.md +218 -0
- data/Rakefile +19 -0
- data/SECURITY.md +20 -0
- data/conformance/core.jsonl +39 -0
- data/conformance/official-r4-core.json +15 -0
- data/conformance/r4.jsonl +6 -0
- data/docs/api.md +170 -0
- data/docs/architecture.md +507 -0
- data/docs/conformance.md +67 -0
- data/docs/feature-matrix.md +49 -0
- data/docs/first-slice.md +57 -0
- data/docs/release-checklist.md +57 -0
- data/docs/releasing.md +87 -0
- data/docs/support-matrix.md +102 -0
- data/lib/fhirpath/ast.rb +128 -0
- data/lib/fhirpath/capability.rb +57 -0
- data/lib/fhirpath/collection.rb +94 -0
- data/lib/fhirpath/compiled_expression.rb +35 -0
- data/lib/fhirpath/conformance/importer.rb +317 -0
- data/lib/fhirpath/errors.rb +76 -0
- data/lib/fhirpath/evaluation_context.rb +30 -0
- data/lib/fhirpath/evaluator.rb +690 -0
- data/lib/fhirpath/functions.rb +71 -0
- data/lib/fhirpath/host_services.rb +52 -0
- data/lib/fhirpath/model.rb +47 -0
- data/lib/fhirpath/model_registry.rb +17 -0
- data/lib/fhirpath/models_r4.rb +91 -0
- data/lib/fhirpath/parser.rb +487 -0
- data/lib/fhirpath/source_span.rb +25 -0
- data/lib/fhirpath/types.rb +73 -0
- data/lib/fhirpath/vector_runner.rb +130 -0
- data/lib/fhirpath/version.rb +9 -0
- data/lib/fhirpath.rb +77 -0
- data/script/import_vectors.rb +14 -0
- data/script/run_vectors.rb +19 -0
- metadata +120 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '0759fa82ecea31aef7e8fb5ffc0c8a1fc023d834ffe49eb296d706bf42ff7ed8'
|
|
4
|
+
data.tar.gz: 0156774a1f73b8099001478f57704e8ee969738454f1019a09d398b9420aa64d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0cd68f2b5a80fd409a066965422a067d1a0664a2bd6c65c215b82c19655a8064ec4da47a54bc4b6767d0f65f7f38ee997815d63796a9097d43df3efcfe6ad448
|
|
7
|
+
data.tar.gz: 42a9413ad029f7a5e77bbfd2e796db4d37debd2152f3f9f1c3e28d4a74100027ef56abb6a63cd2a8bc383c83519546f6d18949f8db38a3dccf73d43aa7b4747b
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
AllCops:
|
|
4
|
+
TargetRubyVersion: 3.2
|
|
5
|
+
NewCops: enable
|
|
6
|
+
Exclude:
|
|
7
|
+
- "vendor/**/*"
|
|
8
|
+
|
|
9
|
+
Style/ArgumentsForwarding:
|
|
10
|
+
Enabled: false
|
|
11
|
+
|
|
12
|
+
Style/SuperArguments:
|
|
13
|
+
Enabled: false
|
|
14
|
+
|
|
15
|
+
Style/ReduceToHash:
|
|
16
|
+
Enabled: false
|
|
17
|
+
|
|
18
|
+
Style/SelectByKind:
|
|
19
|
+
Enabled: false
|
|
20
|
+
|
|
21
|
+
Style/FormatStringToken:
|
|
22
|
+
Enabled: false
|
|
23
|
+
|
|
24
|
+
# The project keeps development dependencies grouped by purpose in Gemfile.
|
|
25
|
+
Bundler/OrderedGems:
|
|
26
|
+
Enabled: false
|
|
27
|
+
|
|
28
|
+
Style/Documentation:
|
|
29
|
+
Enabled: false
|
|
30
|
+
|
|
31
|
+
Metrics/AbcSize:
|
|
32
|
+
Enabled: false
|
|
33
|
+
|
|
34
|
+
Metrics/MethodLength:
|
|
35
|
+
Enabled: false
|
|
36
|
+
|
|
37
|
+
# The public API and context intentionally expose the independently injectable
|
|
38
|
+
# architectural boundaries described in docs/architecture.md.
|
|
39
|
+
Metrics/ParameterLists:
|
|
40
|
+
Exclude:
|
|
41
|
+
- "lib/fhirpath.rb"
|
|
42
|
+
- "lib/fhirpath/evaluation_context.rb"
|
|
43
|
+
- "lib/fhirpath/conformance/importer.rb"
|
|
44
|
+
|
|
45
|
+
# Lexer/parser and evaluator are intentionally cohesive in this first scaffold;
|
|
46
|
+
# future slices can split them once their semantics stabilize.
|
|
47
|
+
Metrics/ClassLength:
|
|
48
|
+
Exclude:
|
|
49
|
+
- "lib/fhirpath/parser.rb"
|
|
50
|
+
- "lib/fhirpath/evaluator.rb"
|
|
51
|
+
- "test/foundation_test.rb"
|
|
52
|
+
- "test/parser_hardening_test.rb"
|
|
53
|
+
- "test/subsetting_functions_test.rb"
|
|
54
|
+
- "lib/fhirpath/conformance/importer.rb"
|
|
55
|
+
- "test/conformance_pipeline_test.rb"
|
|
56
|
+
|
|
57
|
+
Metrics/CyclomaticComplexity:
|
|
58
|
+
Exclude:
|
|
59
|
+
- "lib/fhirpath/parser.rb"
|
|
60
|
+
- "lib/fhirpath/evaluator.rb"
|
|
61
|
+
- "lib/fhirpath/vector_runner.rb"
|
|
62
|
+
|
|
63
|
+
Metrics/PerceivedComplexity:
|
|
64
|
+
Exclude:
|
|
65
|
+
- "lib/fhirpath/parser.rb"
|
|
66
|
+
- "lib/fhirpath/evaluator.rb"
|
|
67
|
+
|
|
68
|
+
# The conformance test deliberately exercises many cases in one readable block
|
|
69
|
+
# for pass/defect classification; split out per-case helpers later if needed.
|
|
70
|
+
Metrics/BlockLength:
|
|
71
|
+
Exclude:
|
|
72
|
+
- "test/conformance_pipeline_test.rb"
|
|
73
|
+
- "fhirpath.gemspec"
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The project is pre-1.0; the API and supported behavior may change between releases.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.2.0.pre1] - 2026-09-05
|
|
8
|
+
- Add the `sum()`, `avg()`, `max()`, and `min()` aggregate functions to the
|
|
9
|
+
standard registry. These are FHIRPath 3.0.0 STU3 aggregate additions
|
|
10
|
+
(published 2026-07-28) and the first STU3-subset functions this project
|
|
11
|
+
ships; they are absent from normative FHIRPath 2.0.0 and from the 3.0.0
|
|
12
|
+
ballot. Semantics follow the published 3.0.0 text where it applies: empty
|
|
13
|
+
input returns the empty collection; non-numeric items raise `TypeError` with
|
|
14
|
+
code `expected_number`; `avg()` converts Integer items to Decimal before
|
|
15
|
+
dividing and always yields a Decimal; and `max()`/`min()` reuse the
|
|
16
|
+
comparison-operator semantics so numerics compare across Integer/Decimal and
|
|
17
|
+
strings order lexicographically, with incompatible item types raising
|
|
18
|
+
`TypeError` with code `incompatible_comparison`. The 3.0.0 text requires all
|
|
19
|
+
items to be the same type; this slice records a documented deviation for
|
|
20
|
+
numeric input instead of an exception: mixed Integer/Decimal input is summed
|
|
21
|
+
in Decimal, matching the engine's numeric promotion in arithmetic and
|
|
22
|
+
equality (`1 = 1.0` and `1 + 2.5` both hold), while all-Integer input keeps
|
|
23
|
+
an Integer `sum()` result. The subset ships by default in the standard
|
|
24
|
+
registry as a declared exception: `Capability.current` keeps `fhirpath`
|
|
25
|
+
`2.0.0` and reports the marker `stu3-aggregate-functions` in `trial_use`;
|
|
26
|
+
`docs/support-matrix.md`, `docs/api.md`, and the feature matrix document the
|
|
27
|
+
exception. `count()` (normative FHIRPath 2.0.0, existence functions)
|
|
28
|
+
already existed and now has focused expression tests and checked-in vectors
|
|
29
|
+
alongside the family. No inputs are mutated and every result is a fresh
|
|
30
|
+
frozen `Collection`; the general-purpose `aggregate()` function remains
|
|
31
|
+
deferred.
|
|
32
|
+
|
|
33
|
+
- Resolve the FHIR logical type of a choice value for the `is`/`as` type
|
|
34
|
+
operators when a model provider is active. `ModelProvider` gains an optional
|
|
35
|
+
`property_logical_type` accessor (safe `nil` default on the base class and
|
|
36
|
+
`PlainModel`); the R4 provider reports the resolved variant's declared type
|
|
37
|
+
from its choice metadata (`valueQuantity` -> `Quantity`, `valueString` ->
|
|
38
|
+
`string`). Navigation records that type per item on the produced collection
|
|
39
|
+
without wrapping or altering the value, so with `model: :r4`,
|
|
40
|
+
`Observation.value is Quantity` yields `[true]` and
|
|
41
|
+
`Observation.value as Quantity` passes the value through unchanged (an empty
|
|
42
|
+
collection when the cast fails). Primitive built-in type tests, empty
|
|
43
|
+
in/empty out for absent choices, and the plain-model default are unchanged.
|
|
44
|
+
`Observation is Resource`/`DomainResource` style tests and `ofType()` remain
|
|
45
|
+
deferred.
|
|
46
|
+
- Add the `tail()`, `take(n)`, and `skip(n)` member functions (FHIRPath 2.0.0, subsetting functions; §5.3.5–5.3.7): `tail()` returns all but the first item and is empty for empty/singleton input; `take(n)` returns the first `min(n, size)` items, empty for `n <= 0` or empty input, and the full collection when `n` exceeds the size; `skip(n)` returns the items after the first `n`, the full collection for `n <= 0`, and empty when no items remain. `take`/`skip` require an integer `n` and raise `TypeError` with code `expected_integer` for a non-integer argument.
|
|
47
|
+
- Add a dependency-free FHIR R4 model provider selectable with `model: :r4`.
|
|
48
|
+
It maps the R4 `Observation.value[x]` JSON variants `valueQuantity` and
|
|
49
|
+
`valueString` to logical `Observation.value`; an absent choice evaluates to
|
|
50
|
+
the empty collection. The provider is exposed as
|
|
51
|
+
`FHIRPath::FHIR::R4::ModelProvider` and advertised by the capability API.
|
|
52
|
+
Resource-type and release matching accept symbol/string and any-case forms.
|
|
53
|
+
- Add the `last()` member function (FHIRPath 2.0.0, existence/navigation functions): it returns the last item in the input collection, an empty collection when the input is empty, and requires zero arguments.
|
|
54
|
+
- Harden the external constant boundary with an injectable `ConstantProvider`, predictable `UnknownConstantError` behavior when absent, generic redacted `HostError` messages without retaining constant-provider exceptions as public causes, and no implicit provider I/O; resolve, terminology, tracing, and cache behavior remain deferred.
|
|
55
|
+
- Correct numeric arithmetic with a zero right operand: `+`, `-`, `*` treat zero as a normal operand, while `/`, `div`, `mod` divide and a zero divisor returns an empty collection (never an error).
|
|
56
|
+
- Correct `union` (`|`) to eliminate duplicate values from both operands using `=` equality in first-seen order, including numerically-equal integers and decimals.
|
|
57
|
+
- Accept a finite JSON `Float` as a `Decimal` for comparison, equality, arithmetic, and the `is Decimal` type test; reject `NaN`/`Infinity` as non-numeric.
|
|
58
|
+
- Correct `contains` so a multi-item right operand raises a singleton-required error even when the searched collection is empty, and so an empty right operand yields an empty result.
|
|
59
|
+
- Correct `&` string concatenation to treat empty operands as empty strings, with regression coverage for both operators' empty-collection behavior.
|
|
60
|
+
- Correct parser precedence for relational, union, and type operators to match the FHIRPath grammar.
|
|
61
|
+
- Harden documentation, package verification, CI, coverage reporting, and contributor workflows.
|
|
62
|
+
- Add the release support matrix, exact package capability metadata, versioning policy, and gated RubyGems/GitHub publication workflow.
|
|
63
|
+
- Declare the project under the MIT License and encode the license in gem metadata.
|
|
64
|
+
- Bound parser recursion: excessively nested expressions (parentheses, unary chains, function calls, indexers, and deep AST-building flat expressions) now raise `FHIRPath::ParseError` with code `nesting_depth_exceeded` instead of letting `SystemStackError` escape the `FHIRPath::Error` boundary.
|
|
65
|
+
- Stop freezing the caller-owned source string: `parse`/`compile` retain an internal frozen snapshot of the expression, so mutating the caller's String afterwards does not affect the compiled program.
|
|
66
|
+
|
|
67
|
+
## [0.1.0.pre1] - 2026-09-04
|
|
68
|
+
|
|
69
|
+
- Added the Ruby-native parser, immutable AST, collection-first evaluator, plain model boundary, structured errors, capability object, and function registry.
|
|
70
|
+
- Added path navigation, literals, arithmetic, selected comparisons, Boolean operators, indexers, filtering, existence functions, and reusable compiled expressions.
|
|
71
|
+
- Added collection-aware equality/equivalence, normalized string and decimal equivalence, empty/not/all functions, string addition, union, membership, primitive type operators, comments, and strict string-escape handling.
|
|
72
|
+
- Added a small JSONL differential-vector runner and six checked-in compatibility vectors.
|
|
73
|
+
- Documented the prototype scope, architecture, limitations, and staged conformance plan.
|
|
74
|
+
|
|
75
|
+
[Unreleased]: https://github.com/niccoreyes/fhirpath-ruby/compare/v0.2.0.pre1...HEAD
|
|
76
|
+
[0.2.0.pre1]: https://github.com/niccoreyes/fhirpath-ruby/releases/tag/v0.2.0.pre1
|
|
77
|
+
[0.1.0.pre1]: https://github.com/niccoreyes/fhirpath-ruby/releases/tag/v0.1.0.pre1
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Contributing to FHIRPath for Ruby
|
|
2
|
+
|
|
3
|
+
Thanks for helping improve the Ruby implementation. It is a pre-1.0 project with a deliberately narrow supported slice, so correctness and honest scope are more important than adding a large number of names quickly.
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
Requirements:
|
|
8
|
+
|
|
9
|
+
- Ruby 3.2 or 3.3 (the supported CI matrix);
|
|
10
|
+
- Bundler; and
|
|
11
|
+
- Git.
|
|
12
|
+
|
|
13
|
+
From a fresh checkout:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
bundle install
|
|
17
|
+
bundle exec rake test
|
|
18
|
+
bundle exec rubocop
|
|
19
|
+
bundle exec rake vectors
|
|
20
|
+
bundle exec rake build
|
|
21
|
+
bundle exec ./script/verify_gem_install.sh pkg/fhirpath-*.gem
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Optional coverage:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
COVERAGE=1 bundle exec rake test
|
|
28
|
+
bundle exec ruby script/check_coverage.rb coverage/summary.json
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The repository intentionally does not require Python for its runtime or vector workflow.
|
|
32
|
+
|
|
33
|
+
## Change workflow
|
|
34
|
+
|
|
35
|
+
1. Open or find an issue describing the behavior or documentation change.
|
|
36
|
+
2. Read `docs/architecture.md`, `docs/feature-matrix.md`, and `docs/conformance.md` before changing evaluator semantics.
|
|
37
|
+
3. For behavior changes, write a focused failing test first (RED), implement the smallest change (GREEN), then run the complete checks above.
|
|
38
|
+
4. Add or update a vector when behavior has an independent reference or specification case.
|
|
39
|
+
5. Update the README, API docs, feature matrix, limitations, and changelog when the public contract changes.
|
|
40
|
+
6. Keep parser, evaluator, model, and host boundaries explicit. Do not add expression-controlled Ruby method dispatch, global evaluator state, or network I/O.
|
|
41
|
+
7. Keep commits focused and do not include build output, credentials, local state, or unrelated work.
|
|
42
|
+
|
|
43
|
+
## Pull requests
|
|
44
|
+
|
|
45
|
+
A useful pull request explains:
|
|
46
|
+
|
|
47
|
+
- the user-visible behavior and the FHIRPath rule it implements;
|
|
48
|
+
- tests added, including expected RED-to-GREEN behavior where relevant;
|
|
49
|
+
- vector/conformance evidence and any host-dependent exclusions;
|
|
50
|
+
- supported Ruby versions exercised; and
|
|
51
|
+
- documentation or compatibility impact.
|
|
52
|
+
|
|
53
|
+
Before requesting review, verify `git diff --check`, `bundle exec rake test`, `bundle exec rubocop`, `bundle exec rake vectors`, `bundle exec rake build`, and the gem-install smoke test. Do not claim complete FHIRPath conformance unless the official suite and release evidence support that claim.
|
|
54
|
+
|
|
55
|
+
For release work, also review [`docs/support-matrix.md`](docs/support-matrix.md),
|
|
56
|
+
[`docs/release-checklist.md`](docs/release-checklist.md), and
|
|
57
|
+
[`docs/releasing.md`](docs/releasing.md). Version changes must update
|
|
58
|
+
`lib/fhirpath/version.rb` and the matching `CHANGELOG.md` section; do not push a
|
|
59
|
+
release tag from a dirty or unreviewed checkout.
|
|
60
|
+
|
|
61
|
+
## Scope and licensing
|
|
62
|
+
|
|
63
|
+
The repository is licensed under the [MIT License](LICENSE), but remains
|
|
64
|
+
pre-release and does not claim complete FHIRPath conformance. Do not represent
|
|
65
|
+
it as a complete or production-ready engine; document compatibility evidence
|
|
66
|
+
and limitations in every behavior change. See `SECURITY.md` for vulnerability
|
|
67
|
+
reports.
|
data/Gemfile
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source 'https://rubygems.org'
|
|
4
|
+
|
|
5
|
+
gemspec
|
|
6
|
+
|
|
7
|
+
# Keep development tooling out of the published gem metadata and use a current
|
|
8
|
+
# RuboCop line compatible with the supported Ruby versions.
|
|
9
|
+
gem 'benchmark', '~> 0.4'
|
|
10
|
+
gem 'minitest', '~> 6.0'
|
|
11
|
+
gem 'rake', '~> 13.0'
|
|
12
|
+
gem 'rubocop', '~> 1.90.0'
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nicco Reyes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# FHIRPath for Ruby
|
|
2
|
+
|
|
3
|
+
[](https://github.com/niccoreyes/fhirpath-ruby/actions/workflows/ci.yml)
|
|
4
|
+
[](https://github.com/niccoreyes/fhirpath-ruby/actions/workflows/release.yml)
|
|
5
|
+
[](https://github.com/niccoreyes/fhirpath-ruby/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/niccoreyes/fhirpath-ruby/blob/main/.github/workflows/ci.yml)
|
|
7
|
+
|
|
8
|
+
A Ruby-native implementation of the [HL7 FHIRPath](https://hl7.org/fhirpath/) expression language.
|
|
9
|
+
|
|
10
|
+
This repository is an intentionally small, pre-release implementation. It provides a tested compatibility slice with stable boundaries for the public API, lexer/parser, immutable AST, collections, evaluation context, plain-model navigation, a dependency-free FHIR R4 model adapter, values, structured errors, and function registration. It does not claim complete FHIRPath conformance or complete FHIR release-model support.
|
|
11
|
+
|
|
12
|
+
## Status at a glance
|
|
13
|
+
|
|
14
|
+
- Version: `0.2.0.pre1`
|
|
15
|
+
- Normative language target: FHIRPath `2.0.0`
|
|
16
|
+
- Ruby support policy: Ruby `3.2` and `3.3` are tested in CI; newer Ruby versions are supported only after CI coverage is added.
|
|
17
|
+
- Release status: pre-release; not published to RubyGems.
|
|
18
|
+
- License: [MIT](LICENSE).
|
|
19
|
+
|
|
20
|
+
The exact release-facing target, capability identifiers, Ruby support matrix,
|
|
21
|
+
and host/model limitations are maintained in the [release support matrix](docs/support-matrix.md).
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
The gem is not yet published. To use a checkout:
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
git clone https://github.com/niccoreyes/fhirpath-ruby.git
|
|
29
|
+
cd fhirpath-ruby
|
|
30
|
+
bundle install
|
|
31
|
+
bundle exec rake test
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Once a release is published, the intended consumer workflow will be:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
# Gemfile
|
|
38
|
+
gem "fhirpath"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The pre-release status and incomplete conformance scope still apply; review the
|
|
42
|
+
documented limitations before using this implementation in production.
|
|
43
|
+
|
|
44
|
+
## Quick start
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
require "fhirpath"
|
|
48
|
+
|
|
49
|
+
patient = {
|
|
50
|
+
"resourceType" => "Patient",
|
|
51
|
+
"name" => [
|
|
52
|
+
{ "use" => "official", "family" => "Lovelace", "given" => ["Ada", "Augusta"] },
|
|
53
|
+
{ "use" => "nickname", "given" => ["Addie"] }
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
FHIRPath.evaluate(patient, "Patient.name.where(use = 'official').given").to_a
|
|
58
|
+
# => ["Ada", "Augusta"]
|
|
59
|
+
|
|
60
|
+
FHIRPath.evaluate_first(patient, "Patient.name.family")
|
|
61
|
+
# => "Lovelace"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
FHIR R4 JSON can be selected explicitly through the versioned provider. The
|
|
65
|
+
adapter exposes the logical `Observation.value` property over R4 choice keys
|
|
66
|
+
such as `valueString` and `valueQuantity`:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
observation = {
|
|
70
|
+
"resourceType" => "Observation",
|
|
71
|
+
"valueQuantity" => { "value" => 120, "unit" => "mmHg" }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
FHIRPath.evaluate(observation, "Observation.value.value", model: :r4).to_a
|
|
75
|
+
# => [120]
|
|
76
|
+
|
|
77
|
+
# The choice variant carries its FHIR logical type, so `is`/`as` resolve
|
|
78
|
+
# against model metadata for the resolved value:
|
|
79
|
+
FHIRPath.evaluate(observation, "Observation.value is Quantity", model: :r4).to_a
|
|
80
|
+
# => [true]
|
|
81
|
+
FHIRPath.evaluate(observation, "Observation.value as Quantity", model: :r4).to_a
|
|
82
|
+
# => [{ "value" => 120, "unit" => "mmHg" }]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The R4 adapter is dependency-free and does not perform Ruby method dispatch;
|
|
86
|
+
plain-model navigation remains the default. Its supported release and model
|
|
87
|
+
selection are visible through `FHIRPath::Capability.current` and
|
|
88
|
+
`FHIRPath.available_models`.
|
|
89
|
+
|
|
90
|
+
## Public API
|
|
91
|
+
|
|
92
|
+
The API is intentionally Ruby-native rather than source-compatible with `fhirpath-py`:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
FHIRPath.parse(expression, capability: FHIRPath::Capability.current)
|
|
96
|
+
FHIRPath.compile(expression, model: nil, capability: ..., functions: ...)
|
|
97
|
+
FHIRPath.evaluate(resource, expression, variables: {}, model: nil,
|
|
98
|
+
capability: ..., functions: ..., options: {}, host: nil)
|
|
99
|
+
FHIRPath.evaluate_first(resource, expression, variables: {}, model: nil,
|
|
100
|
+
capability: ..., functions: ..., options: {}, host: nil)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
A compiled expression is immutable and reusable:
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
program = FHIRPath.compile("Patient.name.family")
|
|
107
|
+
program.evaluate({ "resourceType" => "Patient", "name" => [{ "family" => "Lovelace" }] }).to_a
|
|
108
|
+
# => ["Lovelace"]
|
|
109
|
+
program.call({ "resourceType" => "Patient", "name" => [{ "family" => "Hopper" }] }).to_a
|
|
110
|
+
# => ["Hopper"]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
See [API reference](docs/api.md) for result, error, extension, and immutability contracts, and [architecture](docs/architecture.md) for the implementation boundaries.
|
|
114
|
+
|
|
115
|
+
### Explicit host constants
|
|
116
|
+
|
|
117
|
+
External constant lookup is opt-in and stays behind an injected provider:
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
class TenantConstants < FHIRPath::ConstantProvider
|
|
121
|
+
def fetch(name, mode:, context:)
|
|
122
|
+
constants.fetch(name)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
|
|
127
|
+
def constants
|
|
128
|
+
{ 'tenant' => 'example' }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
host = FHIRPath::HostServices.new(constant_provider: TenantConstants.new)
|
|
133
|
+
FHIRPath.evaluate({}, '%tenant', host: host).to_a
|
|
134
|
+
# => ["example"]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`ConstantProvider#fetch` is the only boundary at which an embedding application may perform external work. The engine does not discover constants, read files, or make network requests. If no provider is supplied, an external constant raises `UnknownConstantError` with code `:unknown_constant`. Constant-provider failures raise a generic `HostError`; exceptions raised by the constant provider are not retained as public causes, and their messages are excluded from the public error serialization and `full_message`. `variables:` takes precedence over the provider, so explicitly supplied values do not trigger provider work.
|
|
138
|
+
|
|
139
|
+
## Supported slice
|
|
140
|
+
|
|
141
|
+
The current tested slice includes:
|
|
142
|
+
|
|
143
|
+
- primitive string, Boolean, integer, decimal, and scientific-notation literals;
|
|
144
|
+
- empty and comma-separated collections;
|
|
145
|
+
- plain Ruby Hash/Array and simple object navigation, including resource-type roots such as `Patient`;
|
|
146
|
+
- dependency-free FHIR R4 model navigation selected with `model: :r4`, including the logical `Observation.value` choice property over `valueQuantity` and `valueString`;
|
|
147
|
+
- unary and numeric arithmetic (`+`, `-`, `*`, `/`, `div`, and `mod`), plus string `+` when both operands are strings; a zero divisor for `/`, `div`, `mod` returns an empty collection, while `+`, `-`, `*` treat zero as a normal operand;
|
|
148
|
+
- numeric/string relational comparison, collection-aware equality, equivalence, and empty-aware Boolean operators; a finite JSON `Float` is treated as a `Decimal`;
|
|
149
|
+
- union, string concatenation (`+` and `&`), membership (`in`/`contains`), and type operators (`is`/`as`); union removes duplicate values from both operands using `=` equality in first-seen order, and `in`/`contains` require a singleton operand; `is`/`as` test built-in primitive types directly and, with `model: :r4`, also resolve the FHIR logical type of a navigated choice value (for example `Observation.value is Quantity` over `valueQuantity`), returning the value unchanged on a successful `as` and the empty collection otherwise; and
|
|
150
|
+
- indexers with non-negative integer indexes;
|
|
151
|
+
- `where`, `select`, `first`, `last`, `tail`, `take`, `skip`, `exists`, `count`, `empty`, `not`, `all`, and Boolean aggregate functions;
|
|
152
|
+
- the FHIRPath 3.0.0 STU3 aggregate functions `sum()`, `avg()`, `max()`, and `min()` (empty input yields the empty collection; `sum`/`avg` require numeric items and `max`/`min` compare numeric and string items with comparison-operator semantics), shipped as a declared, documented exception: `Capability.current` keeps the FHIRPath 2.0.0 target and reports this subset in `trial_use` under the marker `stu3-aggregate-functions`;
|
|
153
|
+
- `$this`, `$index`, and `$total` focus variables;
|
|
154
|
+
- explicitly supplied external constants through `variables:` or an injected `FHIRPath::HostServices` constant provider; and
|
|
155
|
+
- immutable parse/compile boundaries with structured errors and source spans.
|
|
156
|
+
|
|
157
|
+
The [feature matrix](docs/feature-matrix.md) is the executable-scope companion to this list, and the [release support matrix](docs/support-matrix.md) is the publication contract. If a behavior is not listed as supported, callers should handle a specific `FHIRPath::Error` rather than assume permissive fallback.
|
|
158
|
+
|
|
159
|
+
## Explicit limitations
|
|
160
|
+
|
|
161
|
+
This is not yet a complete FHIRPath engine. The following remain deferred or host-dependent:
|
|
162
|
+
|
|
163
|
+
- complete FHIRPath 2.0 conformance; the checked-in importer covers only the pinned official subset;
|
|
164
|
+
- broader FHIR R4 metadata such as primitive extensions, resource-level type tests (`Observation is Resource`/`DomainResource`), `ofType()`, and FHIR R5 model adapters; FHIR R4 `is`/`as` over a resolved choice value's logical type (e.g. `Quantity`) is supported, but terminology and `resolve()` remain host-dependent; and
|
|
165
|
+
- date/time and quantity/UCUM values;
|
|
166
|
+
- advanced conversion, math, string, regular-expression, and navigation functions, and the general-purpose `aggregate()` function;
|
|
167
|
+
- complex literals and additional standard value types;
|
|
168
|
+
- standard environment variables beyond explicitly supplied external constants;
|
|
169
|
+
- FHIRPath 3.0 STU3 features beyond the shipped `sum`/`avg`/`max`/`min` aggregate functions; capability recognition does not enable them silently;
|
|
170
|
+
- network I/O from pure evaluation and global evaluator state; and
|
|
171
|
+
- production support guarantees, until the support matrix and release gates are complete.
|
|
172
|
+
|
|
173
|
+
Unsupported operations raise `UnsupportedFeatureError`, `UnknownFunctionError`, or another specific `FHIRPath::Error`. Malformed or trailing source raises `ParseError`; the parser does not accept a valid prefix and silently ignore trailing input.
|
|
174
|
+
|
|
175
|
+
## Development and verification
|
|
176
|
+
|
|
177
|
+
Install development dependencies and run the complete local checks:
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
bundle install
|
|
181
|
+
bundle exec rake test
|
|
182
|
+
bundle exec rubocop
|
|
183
|
+
bundle exec rake vectors
|
|
184
|
+
bundle exec rake build
|
|
185
|
+
bundle exec ./script/verify_gem_install.sh pkg/fhirpath-*.gem
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The optional vector workflow is deterministic and does not require Python:
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
bundle exec ruby script/run_vectors.rb conformance/core.jsonl
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
It reports `pass`, `defect`, `unsupported`, `host-dependent`, and `not-run` separately. These hand-authored JSONL vectors are compatibility evidence, not a replacement for the official HL7 shared suite. See [Conformance workflow](docs/conformance.md).
|
|
195
|
+
|
|
196
|
+
Coverage is opt-in and uses Ruby's standard `Coverage` library:
|
|
197
|
+
|
|
198
|
+
```sh
|
|
199
|
+
COVERAGE=1 bundle exec rake test
|
|
200
|
+
bundle exec ruby script/check_coverage.rb coverage/summary.json
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
For a clean-checkout reproduction, use the exact commands in [CONTRIBUTING.md](CONTRIBUTING.md). CI runs the supported Ruby matrix, tests, RuboCop, vectors, package build, gem-install smoke test, and coverage report generation.
|
|
204
|
+
|
|
205
|
+
## Contributing
|
|
206
|
+
|
|
207
|
+
Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request. New language behavior should be delivered as a small vertical slice: add a focused failing test, implement the smallest change, run the complete checks, and update the feature matrix and limitations when scope changes.
|
|
208
|
+
|
|
209
|
+
Security reports should follow [SECURITY.md](SECURITY.md). Release readiness,
|
|
210
|
+
versioning, publication, and remaining conformance gates are recorded in
|
|
211
|
+
[`docs/release-checklist.md`](docs/release-checklist.md) and
|
|
212
|
+
[`docs/releasing.md`](docs/releasing.md).
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
This project is licensed under the [MIT License](LICENSE). The gem is still a
|
|
217
|
+
pre-release and does not claim complete FHIRPath conformance; those limitations
|
|
218
|
+
are independent of the license.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rake/testtask'
|
|
5
|
+
|
|
6
|
+
Rake::TestTask.new(:test) do |task|
|
|
7
|
+
task.libs << 'lib'
|
|
8
|
+
task.libs << 'test'
|
|
9
|
+
task.pattern = 'test/**/*_test.rb'
|
|
10
|
+
task.verbose = true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
desc 'Run the checked-in compatibility vectors'
|
|
14
|
+
task :vectors do
|
|
15
|
+
ruby 'script/run_vectors.rb', 'conformance/core.jsonl'
|
|
16
|
+
ruby 'script/run_vectors.rb', 'conformance/r4.jsonl'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
task default: :test
|
data/SECURITY.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Scope
|
|
4
|
+
|
|
5
|
+
This project evaluates expressions against caller-supplied Ruby data. The supported pure-evaluation boundary does not perform network I/O and the plain model intentionally does not dispatch arbitrary Ruby methods selected by an expression.
|
|
6
|
+
|
|
7
|
+
Do not provide production credentials, personal data, or live clinical data in issues, vectors, fixtures, or pull requests.
|
|
8
|
+
|
|
9
|
+
## Reporting a vulnerability
|
|
10
|
+
|
|
11
|
+
Please do not disclose an exploitable vulnerability in a public issue. Use GitHub's private vulnerability reporting or security-advisory channel for this repository when available. If that channel is unavailable, contact the repository owner through GitHub and provide only the minimum reproducible details needed to triage the issue.
|
|
12
|
+
|
|
13
|
+
Reports should include the affected version/commit, Ruby version, expression and fixture reduced to synthetic data, impact, and a suggested mitigation if known. Please allow time for investigation before public disclosure.
|
|
14
|
+
|
|
15
|
+
## Supported versions
|
|
16
|
+
|
|
17
|
+
The project is MIT-licensed but pre-release. Security fixes are evaluated
|
|
18
|
+
against the current `main` branch and the latest published version, if any.
|
|
19
|
+
The gem is not yet published as a production release; see
|
|
20
|
+
`docs/release-checklist.md` for the remaining release gates.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{"id":"collection-equality-001","target":"2.0.0","expression":"left = same","resource":{"left":[1,2],"same":[1,2]},"variables":{},"expected":[true],"origin":{"suite":"fhirpath-py","commit":"19f6316","case":"manual"}}
|
|
2
|
+
{"id":"collection-equivalence-001","target":"2.0.0","expression":"left ~ permuted","resource":{"left":[1,2],"permuted":[2,1]},"variables":{},"expected":[true],"origin":{"suite":"fhirpath-py","commit":"19f6316","case":"manual"}}
|
|
3
|
+
{"id":"string-equivalence-001","target":"2.0.0","expression":"'ab c' ~ 'Ab C'","resource":{},"variables":{},"expected":[true],"origin":{"suite":"fhirpath-py","commit":"19f6316","case":"manual"}}
|
|
4
|
+
{"id":"empty-function-001","target":"2.0.0","expression":"{}.empty()","resource":{},"variables":{},"expected":[true],"origin":{"suite":"fhirpath-py","commit":"19f6316","case":"manual"}}
|
|
5
|
+
{"id":"string-addition-001","target":"2.0.0","expression":"'a' + 'b'","resource":{},"variables":{},"expected":["ab"],"origin":{"suite":"fhirpath-py","commit":"19f6316","case":"manual"}}
|
|
6
|
+
{"id":"type-operator-001","target":"2.0.0","expression":"1 as integer","resource":{},"variables":{},"expected":[1],"origin":{"suite":"fhirpath-py","commit":"19f6316","case":"manual"}}
|
|
7
|
+
{"id":"string-concat-empty-001","target":"2.0.0","expression":"'a' & {}","resource":{},"variables":{},"expected":["a"],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"string-concatenation"}}
|
|
8
|
+
{"id":"type-union-precedence-001","target":"2.0.0","expression":"1 as integer | 2","resource":{},"variables":{},"expected":[1,2],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"operator-precedence"}}
|
|
9
|
+
{"id":"union-dedupe-001","target":"2.0.0","expression":"(1 | 1) | 2","resource":{},"variables":{},"expected":[1,2],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"union-distinct"}}
|
|
10
|
+
{"id":"union-numeric-equality-001","target":"2.0.0","expression":"(1 | 1.0)","resource":{},"variables":{},"expected":[1],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"union-numeric-equality"}}
|
|
11
|
+
{"id":"division-by-zero-empty-001","target":"2.0.0","expression":"5 div 0","resource":{},"variables":{},"expected":[],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"division-by-zero"}}
|
|
12
|
+
{"id":"membership-empty-001","target":"2.0.0","expression":"1 in {}","resource":{},"variables":{},"expected":[false],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"membership-empty"}}
|
|
13
|
+
{"id":"membership-both-empty-001","target":"2.0.0","expression":"{} in {}","resource":{},"variables":{},"expected":[],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"membership-empty-singleton"}}
|
|
14
|
+
{"id":"contains-both-empty-001","target":"2.0.0","expression":"{} contains {}","resource":{},"variables":{},"expected":[],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"membership-empty-singleton"}}
|
|
15
|
+
{"id":"float-as-decimal-001","target":"2.0.0","expression":"value > 1","resource":{"value":1.5},"variables":{},"expected":[true],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"decimal-literal"}}
|
|
16
|
+
{"id":"last-function-001","target":"2.0.0","expression":"(1 | 2 | 3).last()","resource":{},"variables":{},"expected":[3],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"last-function"}}
|
|
17
|
+
{"id":"last-function-empty-001","target":"2.0.0","expression":"{}.last()","resource":{},"variables":{},"expected":[],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"last-function"}}
|
|
18
|
+
{"id":"tail-function-001","target":"2.0.0","expression":"(1 | 2 | 3).tail()","resource":{},"variables":{},"expected":[2,3],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"tail-function"}}
|
|
19
|
+
{"id":"tail-function-empty-001","target":"2.0.0","expression":"{}.tail()","resource":{},"variables":{},"expected":[],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"tail-function"}}
|
|
20
|
+
{"id":"take-function-001","target":"2.0.0","expression":"(1 | 2 | 3).take(2)","resource":{},"variables":{},"expected":[1,2],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"take-function"}}
|
|
21
|
+
{"id":"take-function-empty-001","target":"2.0.0","expression":"{}.take(2)","resource":{},"variables":{},"expected":[],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"take-function"}}
|
|
22
|
+
{"id":"skip-function-001","target":"2.0.0","expression":"(1 | 2 | 3).skip(1)","resource":{},"variables":{},"expected":[2,3],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"skip-function"}}
|
|
23
|
+
{"id":"skip-function-empty-001","target":"2.0.0","expression":"{}.skip(2)","resource":{},"variables":{},"expected":[],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"skip-function"}}
|
|
24
|
+
{"id":"count-function-001","target":"2.0.0","expression":"(1 | 2 | 3).count()","resource":{},"variables":{},"expected":[3],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"count-function"}}
|
|
25
|
+
{"id":"count-function-empty-001","target":"2.0.0","expression":"{}.count()","resource":{},"variables":{},"expected":[0],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"count-function"}}
|
|
26
|
+
{"id":"count-function-navigation-001","target":"2.0.0","expression":"g.count()","resource":{"g":[1,2,3]},"variables":{},"expected":[3],"origin":{"suite":"HL7-FHIRPath","commit":"master","case":"count-function"}}
|
|
27
|
+
{"id":"sum-decimals-001","target":"3.0.0","expression":"(1.0 | 2.0 | 3.0 | 4.0 | 5.0).sum()","resource":{},"variables":{},"expected":[15.0],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"sum-spec-example"}}
|
|
28
|
+
{"id":"sum-integers-001","target":"3.0.0","expression":"(1 | 2 | 3).sum()","resource":{},"variables":{},"expected":[6],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"sum-integers"}}
|
|
29
|
+
{"id":"sum-mixed-numeric-001","target":"3.0.0","expression":"(1 | 2.5).sum()","resource":{},"variables":{},"expected":[3.5],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"sum-mixed-numeric-decision"}}
|
|
30
|
+
{"id":"sum-empty-001","target":"3.0.0","expression":"{}.sum()","resource":{},"variables":{},"expected":[],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"sum-empty-decision"}}
|
|
31
|
+
{"id":"avg-decimals-001","target":"3.0.0","expression":"(5.5 | 4.7 | 4.8).avg()","resource":{},"variables":{},"expected":[5.0],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"avg-spec-example"}}
|
|
32
|
+
{"id":"avg-integers-001","target":"3.0.0","expression":"(1 | 2 | 3 | 4).avg()","resource":{},"variables":{},"expected":[2.5],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"avg-integers-as-decimal"}}
|
|
33
|
+
{"id":"avg-empty-001","target":"3.0.0","expression":"{}.avg()","resource":{},"variables":{},"expected":[],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"avg-empty"}}
|
|
34
|
+
{"id":"min-numeric-001","target":"3.0.0","expression":"(2 | 4 | 8 | 6).min()","resource":{},"variables":{},"expected":[2],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"min-spec-example"}}
|
|
35
|
+
{"id":"min-strings-001","target":"3.0.0","expression":"('b' | 'a' | 'c').min()","resource":{},"variables":{},"expected":["a"],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"min-strings"}}
|
|
36
|
+
{"id":"min-empty-001","target":"3.0.0","expression":"{}.min()","resource":{},"variables":{},"expected":[],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"min-empty"}}
|
|
37
|
+
{"id":"max-numeric-001","target":"3.0.0","expression":"(2 | 4 | 8 | 6).max()","resource":{},"variables":{},"expected":[8],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"max-spec-example"}}
|
|
38
|
+
{"id":"max-strings-001","target":"3.0.0","expression":"('b' | 'a' | 'c').max()","resource":{},"variables":{},"expected":["c"],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"max-strings"}}
|
|
39
|
+
{"id":"max-empty-001","target":"3.0.0","expression":"{}.max()","resource":{},"variables":{},"expected":[],"origin":{"suite":"FHIRPath-3.0.0","commit":"2026-07-28","case":"max-empty"}}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": "fhirpath-conformance-v1",
|
|
3
|
+
"suite": "FHIR/fhir-test-cases",
|
|
4
|
+
"suite_commit": "2d595a506a91eeb311ab787f5fe2c8806be3cfd1",
|
|
5
|
+
"target": "2.0.0",
|
|
6
|
+
"model": "plain",
|
|
7
|
+
"source": "r4/fhirpath/tests-fhir-r4.xml",
|
|
8
|
+
"fixture_root": "r4",
|
|
9
|
+
"cases": [
|
|
10
|
+
"testPrecedence2",
|
|
11
|
+
"testUnion1",
|
|
12
|
+
"testPlus4",
|
|
13
|
+
"testLiteralTrue"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{"id":"r4-observation-value-string-001","target":"2.0.0","model":"R4","expression":"Observation.value","resource":{"resourceType":"Observation","status":"final","valueString":"high"},"variables":{},"expected":["high"],"origin":{"suite":"HL7-FHIR","commit":"R4","case":"Observation.value[x]","url":"https://hl7.org/fhir/R4/observation.html"}}
|
|
2
|
+
{"id":"r4-observation-value-quantity-001","target":"2.0.0","model":"R4","expression":"Observation.value.value","resource":{"resourceType":"Observation","status":"final","valueQuantity":{"value":120,"unit":"mmHg"}},"variables":{},"expected":[120],"origin":{"suite":"HL7-FHIR","commit":"R4","case":"Observation.value[x]","url":"https://hl7.org/fhir/R4/observation.html"}}
|
|
3
|
+
{"id":"r4-observation-value-absent-001","target":"2.0.0","model":"R4","expression":"Observation.value","resource":{"resourceType":"Observation","status":"final"},"variables":{},"expected":[],"origin":{"suite":"HL7-FHIR","commit":"R4","case":"Observation.value[x] absent","url":"https://hl7.org/fhir/R4/observation.html"}}
|
|
4
|
+
{"id":"r4-observation-value-quantity-is-001","target":"2.0.0","model":"R4","expression":"Observation.value is Quantity","resource":{"resourceType":"Observation","status":"final","valueQuantity":{"value":120,"unit":"mmHg"}},"variables":{},"expected":[true],"origin":{"suite":"HL7-FHIR","commit":"R4","case":"Observation.value is Quantity (type operator over choice logical type)","url":"https://hl7.org/fhir/R4/observation.html"}}
|
|
5
|
+
{"id":"r4-observation-value-quantity-as-001","target":"2.0.0","model":"R4","expression":"Observation.value as Quantity","resource":{"resourceType":"Observation","status":"final","valueQuantity":{"value":120,"unit":"mmHg"}},"variables":{},"expected":[{"value":120,"unit":"mmHg"}],"origin":{"suite":"HL7-FHIR","commit":"R4","case":"Observation.value as Quantity passes the value through (type operator over choice logical type)","url":"https://hl7.org/fhir/R4/observation.html"}}
|
|
6
|
+
{"id":"r4-observation-value-absent-type-001","target":"2.0.0","model":"R4","expression":"Observation.value as Quantity","resource":{"resourceType":"Observation","status":"final"},"variables":{},"expected":[],"origin":{"suite":"HL7-FHIR","commit":"R4","case":"Observation.value as Quantity empty in empty out (absent choice)","url":"https://hl7.org/fhir/R4/observation.html"}}
|