A Ruby implementation of the openEHR specifications
Requirements
- Supports the Ruby versions currently maintained upstream by Ruby core: 3.2, 3.4, and 4.0.
- Developed with CRuby 4.0.6 on Linux; CI covers 3.2, 3.4, and 4.0.
- Other versions, including 3.1 and 3.3, are not supported.
=Description
This is a stable release of Ruby openEHR implementation project. This release targets openEHR specification release 1.0.2 as its baseline, with a growing set of RM 1.1.0 / BASE 1.2.0 deltas layered on top (see "RM 1.0.2 vs RM 1.1.0 semantics" below for exactly which classes/attributes are 1.1.0-only). Formerly, we named this package as open_ehr, but changed to openehr from release 1.1.0. 1.0.x versions are obsoleted.
The intention is to have a sample EHR to deploy quickly with Ruby on Rails for development and understanding of the openEHR standards.
This package includes:
- ADL 1.4 parser
- AM/RM packages based on the openEHR specification 1.0.2, plus RM 1.1.0 deltas
- ADL/XML/JSON serializers and an ADL archetype validator
- AQL (Archetype Query Language): a parser plus an in-memory execution engine that evaluates queries directly against RM object graphs you already have in memory (see "Querying with AQL" below)
This package excludes:
- An XML archetype parser (in progress)
- Persistent data stores and search indexes - AQL here only ever queries in-memory RM object graphs; the moment your data lives in a database, that integration (e.g. openehr-rails) owns fetching it out and this gem's AQL engine stays completely unaware of how
- Terminology service(moved to openehr-terminology package)
- Rails plugin(moved to openehr-rails package
Almost all classes passed the test constructed by RSpec23 These spec files are under /spec.
Some specifications are not well determined yet, such as rm/security and so on. We have therefore postponed implementations of these classes.
Terminology and Demographic servers will be implemented in other projects.
=RM 1.0.2 vs RM 1.1.0 semantics
This gem's baseline is openEHR RM 1.0.2, with the following RM 1.1.0 / BASE 1.2.0 deltas incorporated so far. Everything not listed here keeps its 1.0.2 meaning; anything listed only exists, or only behaves this way, from RM 1.1.0 onward.
Class / attribute RM version Notes
------------------------------------------ ---------- ---------------------------------------------
DV_SCALE (new class) 1.1.0 Like DV_ORDINAL, but Real-valued (SPECRM-19)
DV_QUANTITY.units_system 1.1.0 Optional; identifies a non-UCUM units system
DV_QUANTITY.units_display_name 1.1.0 Optional displayable form of units (SPECRM-65)
CODE_PHRASE.preferred_term 1.1.0 Optional preferred term for code_string
DV_PARAGRAPH deprecated Deprecated since RM 1.0.4; still legal, warns
DV_DURATION negative values ("-P10D") 1.1.0 Consistently supported (SPECRM-96)
EHR.folders 1.1.0 List<OBJECT_REF>; directory = folders.item(1)
FOLDER.details 1.1.0 Optional ITEM_STRUCTURE meta-data
FEEDER_AUDIT_DETAILS.other_details 1.1.0 Optional ITEM_STRUCTURE meta-data (SPECRM-74)
ACTIVITY.timing 1.1.0 Now optional (was mandatory pre-1.1.0)
ISM_TRANSITION.reason 1.1.0 Optional List<DV_TEXT>
COMPOSITION.category = "episodic" 1.1.0 A terminology value, not a code change (SPECRM-89);
already supported via the pluggable
OpenEHR::TerminologyService seam
Not yet incorporated from RM 1.1.0 / BASE 1.2.0: ITEM_TAG (still in a development branch, not yet in a released RM version) and the finer points of DV_DURATION arithmetic beyond negation/unary minus.
=Querying with AQL
query = OpenEHR::AQL.parse(aql_string) # => a parsed, immutable Query
result = query.execute(dataset, params: { ehr_id: '...' }) # => a ResultSet (Enumerable, plus #to_json)
result = OpenEHR::AQL.execute(aql_string, dataset, params: {}) # one-shot
The engine evaluates queries against OpenEHR::AQL::Dataset, its one and only input boundary - and the whole of its independence from any particular web framework or O/R mapper. Everything under lib/openehr/aql/ talks to "wherever your data lives" exclusively through Dataset, which itself depends on nothing but Ruby's own Enumerable/#each protocol and this gem's own RM classes - no ActiveRecord, no Rails, no Sequel, anywhere in this gem, ever.
==Supplying data to AQL
Dataset accepts any Enumerable of records shaped like {ehr_id:, compositions:, ehr_status: (optional)} (a Hash with symbol or string keys, or a duck-typed object responding to the same methods), or a full OpenEHR::RM::EHR::EHR if you already have one. Every element yielded by a record's compositions must be one of this gem's own RM objects (is_a?(OpenEHR::RM::Common::Archetyped::Pathable)) - anything else raises OpenEHR::AQL::DatasetError immediately, naming the offending class and index, rather than half-working via loose duck typing.
Construction never iterates its source, so a lazily-produced Enumerable stays lazy all the way through execute. A hypothetical Rails + ActiveRecord integration (e.g. openehr-rails) might look like this - note this is entirely outside this gem, shown only to make the boundary concrete:
records = Ehr.find_each.lazy.map { |row|
{ ehr_id: row.uid,
compositions: row.composition_records.lazy.map { |cr|
OpenEHR::RM::CompositionFactory.create_from_json(cr.canonical_json) } } }
OpenEHR::AQL.execute(aql, OpenEHR::AQL::Dataset.new(ehrs: records))
Swapping to Sinatra + Sequel, or any other framework/ORM, only changes the two .map bodies above - nothing in this gem's AQL code needs to know or care. For quick, non-database scripts:
c1 = OpenEHR::RM::CompositionFactory.create_from_json(composition_json) # or build one by hand
OpenEHR::AQL.execute(aql, OpenEHR::AQL::Dataset.of_compositions([c1, c2]))
# or even just: OpenEHR::AQL.execute(aql, [c1, c2]) # via Dataset.wrap
=Main codebase authors
- Shinji Kobayashi and Akimichi Tatsukawa of http://openehr.jp
- ADL parser test cases are derived from Java and Eiffel reference implementation project of the openEHR.
=Contributors Thanks for pull requests on GitHub.
- Michael Deryugin
- Dmitry Lavrov
- Evgeny Strokov
- Marcus Baw(pacharanero)
=Copyright The software, including all files in this directory and subdirectories except ADL files is copyrighted to the original authors and contributors, 2011-2013.
All Rights Reserved.
=License
This product is released under Apache 2.0 license
Copyright [2012-2020] openEHR Ruby implementation project.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.