simple_hl7 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +188 -0
- data/Rakefile +2 -0
- data/lib/simple_hl7.rb +15 -0
- data/lib/simple_hl7/component.rb +11 -0
- data/lib/simple_hl7/component_container.rb +11 -0
- data/lib/simple_hl7/composite.rb +170 -0
- data/lib/simple_hl7/field.rb +23 -0
- data/lib/simple_hl7/message.rb +74 -0
- data/lib/simple_hl7/msh_segment.rb +38 -0
- data/lib/simple_hl7/segment.rb +23 -0
- data/lib/simple_hl7/separator_characters.rb +8 -0
- data/lib/simple_hl7/subcomponent.rb +27 -0
- data/lib/simple_hl7/version.rb +3 -0
- data/simple_hl7.gemspec +20 -0
- data/spec/simple_hl7/component_container_spec.rb +22 -0
- data/spec/simple_hl7/component_spec.rb +30 -0
- data/spec/simple_hl7/composite_spec.rb +0 -0
- data/spec/simple_hl7/field_spec.rb +15 -0
- data/spec/simple_hl7/message_spec.rb +37 -0
- data/spec/simple_hl7/msh_segment_spec.rb +13 -0
- data/spec/simple_hl7/segment_spec.rb +23 -0
- data/spec/simple_hl7/subcomponent_spec.rb +32 -0
- metadata +110 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Rome Portlock
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
# SimpleHl7
|
2
|
+
|
3
|
+
SimpleHL7 is a library that manages HL7 v2.x documents for interfacing with
|
4
|
+
health care systems. The goal of SimpleHL7 is to make it easy to create basic
|
5
|
+
HL7 messages while also having the power to create more complex ones. SimpleHL7
|
6
|
+
is agnostic of message and segment types, it works only with the basic
|
7
|
+
structure of HL7 documents.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'simple_hl7'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install simple_hl7
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
SimpleHL7 can be used for either message creation or parsing.
|
26
|
+
|
27
|
+
### Message Creation
|
28
|
+
|
29
|
+
A simple example:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
msg = SimpleHL7::Message.new
|
33
|
+
msg.msh[9][1] = "ADT"
|
34
|
+
msg.msh[9][2] = "A04"
|
35
|
+
msg.msh[10] = "12345678"
|
36
|
+
msg.msh[11] = "D"
|
37
|
+
msg.msh[12] = "2.5"
|
38
|
+
|
39
|
+
msg.pid[3] = "454545"
|
40
|
+
msg.pid[5][1] = "Doe"
|
41
|
+
msg.pid[5][2] = "John"
|
42
|
+
|
43
|
+
msg.pv1[2] = "O"
|
44
|
+
|
45
|
+
msg.to_hl7
|
46
|
+
```
|
47
|
+
|
48
|
+
Would generate the following HL7 string.
|
49
|
+
|
50
|
+
```
|
51
|
+
MSH|^~\&|||||||ADT^A04|12345678|D|2.5
|
52
|
+
PID|||454545||Doe^John
|
53
|
+
PV1||O
|
54
|
+
```
|
55
|
+
|
56
|
+
This is the easiest way to use SimpleHL7, however most of the methods used
|
57
|
+
above are syntactic sugar for underlying methods that are explained in detail
|
58
|
+
later.
|
59
|
+
|
60
|
+
### Adding a segment
|
61
|
+
|
62
|
+
The easiest way to add a segment to a new message is by calling its three
|
63
|
+
letter segment name as a method on the message. For example to create a PID
|
64
|
+
segment, do the following:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
msg = SimpleHL7::Message.new
|
68
|
+
msg.pid
|
69
|
+
```
|
70
|
+
|
71
|
+
Note that since the MSH segment is always required it is created automatically.
|
72
|
+
So if to_hl7 was called on the message above, the result would be
|
73
|
+
|
74
|
+
```
|
75
|
+
MSH|^~\&|
|
76
|
+
PID
|
77
|
+
```
|
78
|
+
|
79
|
+
### Repeating segments
|
80
|
+
|
81
|
+
Using the segment name is the easiest way to create a new segment, but if you
|
82
|
+
have more than one segment with the same name it won't work. The first name
|
83
|
+
method call will create a segment, but subsequent calls will just reference
|
84
|
+
that first created segment.
|
85
|
+
|
86
|
+
To create multiple segments of the same type use the underlying `add_segment`
|
87
|
+
method.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
obx1 = msg.add_segment('obx')
|
91
|
+
obx2 = msg.add_segment('obx')
|
92
|
+
```
|
93
|
+
|
94
|
+
To later retreive a certain segment use the `segment` method.
|
95
|
+
|
96
|
+
```
|
97
|
+
obx2 = msg.segment('obx', 2)
|
98
|
+
```
|
99
|
+
|
100
|
+
### Adding components, subcomponents, fields etc.
|
101
|
+
|
102
|
+
To add values to the message just specifiy the index in brackets
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
msg = SimpleHL7::Message.new
|
106
|
+
msg.msh[12] = '2.5'
|
107
|
+
```
|
108
|
+
|
109
|
+
To specifiy a certain component use more brackets.
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
msg = SimpleHL7::Message.new
|
113
|
+
msg.msh[9][1] = "ADT"
|
114
|
+
msg.msh[9][2] = "A04"
|
115
|
+
```
|
116
|
+
|
117
|
+
It is important to note that under the hood the the bracket syntax actually
|
118
|
+
adds the value to the first subcomponent of the first componenet of the first
|
119
|
+
repition in the specified field.
|
120
|
+
|
121
|
+
This means that:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
msg.msh[9] = "ADT"
|
125
|
+
msg.msh[9][1] = "ADT"
|
126
|
+
msg.msh[9][1][1] = "ADT"
|
127
|
+
```
|
128
|
+
|
129
|
+
are all equivalent.
|
130
|
+
|
131
|
+
### Repeating fields
|
132
|
+
|
133
|
+
Since repeating fields are less common in HL7 they require a little bit of
|
134
|
+
extra work to create using SimpleHL7. Use the `r(index)` method to create
|
135
|
+
repeats.
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
msg.pid[13] = '123-4567'
|
139
|
+
msg.pid[13].r(2)[1] = '876-5432'
|
140
|
+
```
|
141
|
+
|
142
|
+
Creates the following PID segment
|
143
|
+
|
144
|
+
```
|
145
|
+
PID|||||||||||||123-4567~876-5432
|
146
|
+
```
|
147
|
+
|
148
|
+
### HL7 Escaping
|
149
|
+
|
150
|
+
HL7 special characters are automatically escaped properly when generating HL7,
|
151
|
+
without doing any extra work.
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
msg = SimpleHL7::Message.new
|
155
|
+
msg.nte[3] = "Testing & escaping notes"
|
156
|
+
msg.to_hl7
|
157
|
+
```
|
158
|
+
|
159
|
+
Outputs:
|
160
|
+
|
161
|
+
```
|
162
|
+
MSH|^~\&|
|
163
|
+
NTE|||Testing \T\ escaping notes
|
164
|
+
```
|
165
|
+
|
166
|
+
### Parsing
|
167
|
+
|
168
|
+
To parse HL7 string use the parse method
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
hl7_str = "MSH|^~\\&|||||||ADT^A04|12345678|D|2.5\rPID|||454545||Doe^John"
|
172
|
+
msg = SimpleHL7::Message.parse(hl7_str)
|
173
|
+
```
|
174
|
+
|
175
|
+
Once the message is parsed use to_s to pull out values
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
msg.pid[5][1].to_s
|
179
|
+
=> "Doe"
|
180
|
+
```
|
181
|
+
|
182
|
+
## Contributing
|
183
|
+
|
184
|
+
1. Fork it
|
185
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
186
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
187
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
188
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/simple_hl7.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "simple_hl7/version"
|
2
|
+
|
3
|
+
module SimpleHL7
|
4
|
+
# Your code goes here...
|
5
|
+
end
|
6
|
+
|
7
|
+
require "simple_hl7/separator_characters"
|
8
|
+
require "simple_hl7/composite"
|
9
|
+
require "simple_hl7/subcomponent"
|
10
|
+
require "simple_hl7/component"
|
11
|
+
require "simple_hl7/component_container"
|
12
|
+
require "simple_hl7/field"
|
13
|
+
require "simple_hl7/segment"
|
14
|
+
require "simple_hl7/msh_segment"
|
15
|
+
require "simple_hl7/message"
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module SimpleHL7
|
2
|
+
# Generic building block of a HL7 message. The parts of the message
|
3
|
+
# subclass this, and this class does most of the work.
|
4
|
+
class Composite
|
5
|
+
# Constructor
|
6
|
+
#
|
7
|
+
# @param value [String] a value that is passed down to the subclass
|
8
|
+
# constructor. This allows us to set values on top level components that
|
9
|
+
# are passed down to the lowest component. Default nil.
|
10
|
+
def initialize(value = nil)
|
11
|
+
@subcomposites = {}
|
12
|
+
cls = self.class
|
13
|
+
unless value.nil?
|
14
|
+
@subcomposites[cls.start_index] = cls.subcomposite_class.new(value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Set the value of the specified subcomposite.
|
19
|
+
# The value is actually passed down to the first subcomposite of the
|
20
|
+
# specified subcomposite and so on until it reaches a Subcomponent
|
21
|
+
# composite.
|
22
|
+
#
|
23
|
+
# @param index [Integer] the subcomposite index.
|
24
|
+
# @param value [String] the value to set.
|
25
|
+
def []=(index, value)
|
26
|
+
set_subcomposite(index, self.class.subcomposite_class.new(value))
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get a specific subcomposite.
|
30
|
+
#
|
31
|
+
# @param index [Integer] The index of the subcomposite.
|
32
|
+
# @return [Subcomposite] The subcomposite at index or a new subcomposite
|
33
|
+
# if none exists. Note that this returns the Subcomposite object and not
|
34
|
+
# the string value. This differs from how []= works, but it seems to make
|
35
|
+
# the most sense when dealing with HL7 messages.
|
36
|
+
def [](index)
|
37
|
+
get_subcomposite(index)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Alias for []
|
41
|
+
def get_subcomposite(index)
|
42
|
+
subcomposite = @subcomposites[index]
|
43
|
+
if subcomposite.nil?
|
44
|
+
subcomposite = self.class.subcomposite_class.new
|
45
|
+
set_subcomposite(index, subcomposite)
|
46
|
+
end
|
47
|
+
subcomposite
|
48
|
+
end
|
49
|
+
|
50
|
+
# Sets a specific subcomposite
|
51
|
+
#
|
52
|
+
# @param index [Integer] the indexs of the subcomposite, if there is an
|
53
|
+
# existing compsite at the location it is replaced.
|
54
|
+
# @param value [Subcomposite] the new subcomposite object for the
|
55
|
+
# specified location.
|
56
|
+
def set_subcomposite(index, value)
|
57
|
+
@subcomposites[index] = value
|
58
|
+
end
|
59
|
+
|
60
|
+
# Calls the specified block once for each index between the start index
|
61
|
+
# and the max specified subcomposite index.
|
62
|
+
#
|
63
|
+
# @yeild [subcomposite] Gives the subcomposite at the current index to the
|
64
|
+
# block. If there isn't a subcomposite specified for the index then nil
|
65
|
+
# is passed.
|
66
|
+
def each
|
67
|
+
start = self.class.start_index
|
68
|
+
(start..max_index).each { |i| yield @subcomposites[i] } if max_index
|
69
|
+
end
|
70
|
+
|
71
|
+
# Calls the specified block once for each index between the start index
|
72
|
+
# and the max specified subcomposite index and returns an array resulting
|
73
|
+
# from the return values of each block.
|
74
|
+
#
|
75
|
+
# @yeild [subcomposite] Gives the subcomposite at the current index to the
|
76
|
+
# block. If there isn't a subcomposite specified for the index then nil
|
77
|
+
# is passed.
|
78
|
+
def map
|
79
|
+
start = self.class.start_index
|
80
|
+
(start..max_index).map { |i| yield @subcomposites[i] } if max_index
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get a HL7 string representation of this Composite.
|
84
|
+
#
|
85
|
+
# @separator_chars [SeparatorChars] The separator characters to be used
|
86
|
+
# when converting this Composite to a string of HL7.
|
87
|
+
def to_hl7(separator_chars)
|
88
|
+
sep_char = self.class.current_separator_char(separator_chars)
|
89
|
+
map { |subc| subc.to_hl7(separator_chars) if subc }.join(sep_char)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Get the value stored at the first Subcomponent below this Composite
|
93
|
+
def to_s
|
94
|
+
@subcomposites[self.class.start_index].to_s
|
95
|
+
end
|
96
|
+
|
97
|
+
# Get all the subcomposites as an array. Note that this array has the
|
98
|
+
# actual subcomposite object and not clones, so any changes will affect
|
99
|
+
# this class as well.
|
100
|
+
def to_a
|
101
|
+
a = []
|
102
|
+
each {|subc| a << subc}
|
103
|
+
a
|
104
|
+
end
|
105
|
+
|
106
|
+
# The index where the first subcomposite is located. This is usually either
|
107
|
+
# 1 or 0 depending on the specific Composite.
|
108
|
+
def self.start_index
|
109
|
+
1
|
110
|
+
end
|
111
|
+
|
112
|
+
# @abstract The character that is used to separate the subcomposites when
|
113
|
+
# generating a HL7 string.
|
114
|
+
#
|
115
|
+
# @param separator_chars [SeparatorChars] The separator characters in use
|
116
|
+
# during the HL7 string conversion.
|
117
|
+
def self.current_separator_char(separator_chars)
|
118
|
+
raise Exception.new("Subclass Responsibility")
|
119
|
+
end
|
120
|
+
|
121
|
+
# @abstract The class that is used for subcomposites.
|
122
|
+
def self.subcomposite_class
|
123
|
+
raise Exception.new("Subclass Responsibility")
|
124
|
+
end
|
125
|
+
|
126
|
+
# Create a composite from a HL7 string.
|
127
|
+
#
|
128
|
+
# @param str [String] The string of HL7.
|
129
|
+
# @param separator_chars [SeparatorChars] The separator characters used
|
130
|
+
# in the HL7 string.
|
131
|
+
# @return [Composite] The parsed composite.
|
132
|
+
def self.parse(str, separator_chars)
|
133
|
+
composite = new
|
134
|
+
parse_subcomposite_hash(str, separator_chars).each do |index, subc|
|
135
|
+
composite.set_subcomposite(start_index + index, subc)
|
136
|
+
end
|
137
|
+
composite
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def max_index
|
143
|
+
@subcomposites.keys.max
|
144
|
+
end
|
145
|
+
|
146
|
+
class << self
|
147
|
+
private
|
148
|
+
|
149
|
+
# Parses the subcomposites of a HL7 string into a hash
|
150
|
+
#
|
151
|
+
# @param str [String] The HL7 string to parse.
|
152
|
+
# @param separator_chars [SeparatorChars] The separator characters used in
|
153
|
+
# the HL7 string.
|
154
|
+
# @return A hash of composites, one for each subcomposite in the string
|
155
|
+
# that actually had a value. Empty subcomposites are left out of the
|
156
|
+
# hash.
|
157
|
+
def parse_subcomposite_hash(str, separator_chars)
|
158
|
+
subc_strs = str.split(current_separator_char(separator_chars))
|
159
|
+
subc_h = {}
|
160
|
+
subc_strs.each_with_index do |subc_str, index|
|
161
|
+
unless subc_str.empty?
|
162
|
+
subc_h[index] = subcomposite_class.parse(subc_str, separator_chars)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
subc_h
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SimpleHL7
|
2
|
+
class Field < Composite
|
3
|
+
def self.current_separator_char(separator_chars)
|
4
|
+
separator_chars.repetition
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.subcomposite_class
|
8
|
+
ComponentContainer
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(index, value)
|
12
|
+
get_subcomposite(1)[index] = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](index)
|
16
|
+
get_subcomposite(1)[index]
|
17
|
+
end
|
18
|
+
|
19
|
+
def r(index)
|
20
|
+
get_subcomposite(index)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module SimpleHL7
|
2
|
+
class Message
|
3
|
+
def initialize(default_msh = true)
|
4
|
+
@segments = []
|
5
|
+
@segments << MSHSegment.new if default_msh
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(meth, *args, &block)
|
9
|
+
if meth.to_s =~ /^[a-zA-Z][a-zA-Z0-9]{2}$/
|
10
|
+
get_named_segment(meth)
|
11
|
+
elsif meth.to_s =~ /^[a-zA-Z][a-zA-Z0-9]{2}_all$/
|
12
|
+
seg_name = meth[0..3]
|
13
|
+
all_segments(seg_name)
|
14
|
+
else
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hl7
|
20
|
+
separator_chars = get_named_segment('MSH').separator_chars
|
21
|
+
@segments.map {|s| s.to_hl7(separator_chars)}.join("\r")
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_a
|
25
|
+
@segments.reduce([]) {|a, s| a << s.to_a}
|
26
|
+
end
|
27
|
+
|
28
|
+
def segment(name, index=1)
|
29
|
+
all = all_segments(name)
|
30
|
+
seg = nil
|
31
|
+
seg = all[index - 1] if all.size >= index
|
32
|
+
seg
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_segment(name)
|
36
|
+
segment = Segment.new(name)
|
37
|
+
@segments << segment
|
38
|
+
segment
|
39
|
+
end
|
40
|
+
|
41
|
+
def append_segment(segment)
|
42
|
+
@segments << segment
|
43
|
+
segment
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def all_segments(name)
|
49
|
+
@segments.select {|seg| seg.name == name}
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def get_named_segment(name)
|
54
|
+
name_str = name.to_s.upcase
|
55
|
+
segment = @segments.select {|seg| seg.name == name_str}.first
|
56
|
+
unless segment
|
57
|
+
segment = Segment.new(name_str)
|
58
|
+
@segments << segment
|
59
|
+
end
|
60
|
+
segment
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.parse(str)
|
64
|
+
msg = new(false)
|
65
|
+
segment_strs = str.split("\r")
|
66
|
+
msh = MSHSegment.parse(segment_strs[0])
|
67
|
+
msg.append_segment(msh)
|
68
|
+
segment_strs[1, segment_strs.length].each do |seg_str|
|
69
|
+
msg.append_segment(Segment.parse(seg_str, msh.separator_chars))
|
70
|
+
end
|
71
|
+
msg
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module SimpleHL7
|
2
|
+
class MSHSegment < Segment
|
3
|
+
def initialize
|
4
|
+
super('MSH')
|
5
|
+
self[1] = '|'
|
6
|
+
self[2] = '^~\&'
|
7
|
+
end
|
8
|
+
|
9
|
+
def separator_chars
|
10
|
+
enc_chars = @subcomposites[2].to_s
|
11
|
+
SeparatorCharacters.new(@subcomposites[1].to_s,
|
12
|
+
enc_chars[0],
|
13
|
+
enc_chars[1],
|
14
|
+
enc_chars[2],
|
15
|
+
enc_chars[3])
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.parse(str)
|
19
|
+
msh = new
|
20
|
+
msh[1] = str[3]
|
21
|
+
msh[2] = str[4..7]
|
22
|
+
|
23
|
+
fields = parse_subcomposite_hash(str[9, str.length], msh.separator_chars)
|
24
|
+
fields.each { |index, subc| msh.set_subcomposite(index + 3, subc) }
|
25
|
+
msh
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_hl7(separator_chars)
|
29
|
+
sep_char = self.class.current_separator_char(separator_chars)
|
30
|
+
base_msh = "#{name}#{self[1]}#{self[2]}"
|
31
|
+
max_index = @subcomposites.keys.max
|
32
|
+
rest_msh = (3..max_index).map { |i|
|
33
|
+
@subcomposites[i].to_hl7(separator_chars) if @subcomposites[i]
|
34
|
+
}.join(sep_char)
|
35
|
+
[base_msh, rest_msh].join(sep_char)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SimpleHL7
|
2
|
+
class Segment < Composite
|
3
|
+
def self.start_index
|
4
|
+
0
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.subcomposite_class
|
8
|
+
Field
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.current_separator_char(separator_chars)
|
12
|
+
separator_chars.field
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
self[0].to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_a
|
20
|
+
super.insert(0, name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module SimpleHL7
|
2
|
+
class Subcomponent < Struct.new(:value)
|
3
|
+
def to_hl7(separator_chars)
|
4
|
+
hl7 = value
|
5
|
+
hl7.gsub!(separator_chars.escape, "\\E\\")
|
6
|
+
hl7.gsub!(separator_chars.field, "\\F\\")
|
7
|
+
hl7.gsub!(separator_chars.repetition, "\\R\\")
|
8
|
+
hl7.gsub!(separator_chars.component, "\\S\\")
|
9
|
+
hl7.gsub!(separator_chars.subcomponent, "\\T\\")
|
10
|
+
hl7
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
value
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.parse(str, separator_chars)
|
18
|
+
value = str
|
19
|
+
value.gsub!("\\E\\", separator_chars.escape)
|
20
|
+
value.gsub!("\\F\\", separator_chars.field)
|
21
|
+
value.gsub!("\\R\\", separator_chars.repetition)
|
22
|
+
value.gsub!("\\S\\", separator_chars.component)
|
23
|
+
value.gsub!("\\T\\", separator_chars.subcomponent)
|
24
|
+
Subcomponent.new(value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/simple_hl7.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/simple_hl7/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Rome Portlock"]
|
6
|
+
gem.email = ["rome@alivecor.com"]
|
7
|
+
gem.description = %q{Parse and generate hl7 messages for interfacing with health care systems}
|
8
|
+
gem.summary = %q{Parse and generate hl7 messages}
|
9
|
+
gem.homepage = "https://github.com/alivecor/simple_hl7"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "simple_hl7"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = SimpleHL7::VERSION
|
17
|
+
|
18
|
+
gem.add_development_dependency "pry", "~> 0.9"
|
19
|
+
gem.add_development_dependency "rspec", "~> 2.14"
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "simple_hl7"
|
2
|
+
|
3
|
+
module SimpleHL7
|
4
|
+
describe ComponentContainer do
|
5
|
+
describe "#to_hl7" do
|
6
|
+
it "generates proper hl7 for a field with subfields" do
|
7
|
+
container = ComponentContainer.new
|
8
|
+
container[1] = "foo"
|
9
|
+
container[2] = "bar"
|
10
|
+
container.to_hl7(SeparatorCharacters.defaults).should == "foo^bar"
|
11
|
+
end
|
12
|
+
|
13
|
+
it "generates proper hl7 for a field with components and subfields" do
|
14
|
+
container = ComponentContainer.new
|
15
|
+
container[1] = "foo"
|
16
|
+
container[1][2] = "bar"
|
17
|
+
container[2] = "baz"
|
18
|
+
container.to_hl7(SeparatorCharacters.defaults).should == "foo&bar^baz"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "simple_hl7"
|
2
|
+
|
3
|
+
module SimpleHL7
|
4
|
+
describe Component do
|
5
|
+
describe "#to_hl7" do
|
6
|
+
it "generates proper hl7 for a field with subfields" do
|
7
|
+
field = Component.new
|
8
|
+
field[1] = "foo"
|
9
|
+
field[2] = "bar"
|
10
|
+
field.to_hl7(SeparatorCharacters.defaults).should == "foo&bar"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "[]" do
|
15
|
+
it "gets the proper value" do
|
16
|
+
field = Component.new
|
17
|
+
field[1] = "foo"
|
18
|
+
field[1].to_s.should == "foo"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#parse" do
|
23
|
+
it "parse hl7 correctly" do
|
24
|
+
component = Component.parse("foo&bar", SeparatorCharacters.defaults)
|
25
|
+
component[1].to_s.should == "foo"
|
26
|
+
component[2].to_s.should == "bar"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "simple_hl7"
|
2
|
+
|
3
|
+
module SimpleHL7
|
4
|
+
describe Field do
|
5
|
+
describe "#to_hl7" do
|
6
|
+
it "generates proper hl7 for a field with subfields" do
|
7
|
+
field = Field.new
|
8
|
+
field[1] = "foo"
|
9
|
+
field[2] = "baz"
|
10
|
+
field.r(2)[1] = "bar"
|
11
|
+
field.to_hl7(SeparatorCharacters.defaults).should == 'foo^baz~bar'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "simple_hl7"
|
2
|
+
|
3
|
+
module SimpleHL7
|
4
|
+
describe Message do
|
5
|
+
describe "#to_hl7" do
|
6
|
+
it "generates an hl7 message" do
|
7
|
+
msg = Message.new
|
8
|
+
msg.msh[6] = "accountid"
|
9
|
+
msg.pid[5] = "User"
|
10
|
+
msg.pid[5][2] = "Test"
|
11
|
+
msg.to_hl7.should == "MSH|^~\\&||||accountid\rPID|||||User^Test"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#add_segment" do
|
16
|
+
it "adds segments properly" do
|
17
|
+
msg = Message.new
|
18
|
+
obx_1 = msg.add_segment('OBX')
|
19
|
+
obx_1[1] = "1"
|
20
|
+
obx_2 = msg.add_segment('OBX')
|
21
|
+
obx_2[1] = "2"
|
22
|
+
msg.segment('OBX', 1)[1].to_s.should == "1"
|
23
|
+
msg.segment('OBX', 2)[1].to_s.should == "2"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#parse" do
|
28
|
+
it "properly parses a hl7 string" do
|
29
|
+
msg = Message.parse("MSH|^~\\&||||accountid\rPID|||||User^Test~Repeat")
|
30
|
+
msg.msh[6].to_s.should == "accountid"
|
31
|
+
msg.pid[5].to_s.should == "User"
|
32
|
+
msg.pid[5][2].to_s.should == "Test"
|
33
|
+
msg.pid[5].r(2)[1].to_s.should == "Repeat"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "simple_hl7"
|
2
|
+
|
3
|
+
module SimpleHL7
|
4
|
+
describe MSHSegment do
|
5
|
+
describe "#parse" do
|
6
|
+
it "parses a hl7 string correctly" do
|
7
|
+
msh = MSHSegment.parse('MSH|^~\\&|||test')
|
8
|
+
msh.separator_chars.field.should == '|'
|
9
|
+
msh[5].to_s.should == 'test'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "pry"
|
2
|
+
require "simple_hl7"
|
3
|
+
|
4
|
+
module SimpleHL7
|
5
|
+
describe Segment do
|
6
|
+
describe "#to_hl7" do
|
7
|
+
it "generates a message using the specified separator chars" do
|
8
|
+
sep_chars = SeparatorCharacters.defaults
|
9
|
+
seg = Segment.new('PID')
|
10
|
+
seg[5] = 'test'
|
11
|
+
seg.to_hl7(sep_chars).should == 'PID|||||test'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#parse" do
|
16
|
+
it "parses a hl7 string correctly" do
|
17
|
+
sep_chars = SeparatorCharacters.defaults
|
18
|
+
seg = Segment.parse('PID|||||test', sep_chars)
|
19
|
+
seg[5].to_s.should == 'test'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "simple_hl7"
|
2
|
+
|
3
|
+
module SimpleHL7
|
4
|
+
describe Subcomponent do
|
5
|
+
describe "#to_hl7" do
|
6
|
+
it "returns the value" do
|
7
|
+
subf = Subcomponent.new('test')
|
8
|
+
subf.to_hl7(SeparatorCharacters.defaults).should == 'test'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "escapes special characters in a string" do
|
12
|
+
subc = Subcomponent.new('peas&carrots')
|
13
|
+
subc.to_hl7(SeparatorCharacters.defaults).should == 'peas\T\carrots'
|
14
|
+
end
|
15
|
+
|
16
|
+
it "escapes all special characters" do
|
17
|
+
subc = Subcomponent.new('\\&^~|')
|
18
|
+
default_chars = SeparatorCharacters.defaults
|
19
|
+
subc.to_hl7(default_chars).should == "\\E\\\\T\\\\S\\\\R\\\\F\\"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#parse" do
|
24
|
+
it "should unescape the special characters" do
|
25
|
+
str = "\\E\\\\T\\\\S\\\\R\\\\F\\"
|
26
|
+
default_chars = SeparatorCharacters.defaults
|
27
|
+
parsed = Subcomponent.parse(str, default_chars)
|
28
|
+
parsed.to_s.should == '\\&^~|'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_hl7
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Rome Portlock
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-02-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: pry
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.9'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.9'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '2.14'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '2.14'
|
46
|
+
description: Parse and generate hl7 messages for interfacing with health care systems
|
47
|
+
email:
|
48
|
+
- rome@alivecor.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- .gitignore
|
54
|
+
- Gemfile
|
55
|
+
- LICENSE
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- lib/simple_hl7.rb
|
59
|
+
- lib/simple_hl7/component.rb
|
60
|
+
- lib/simple_hl7/component_container.rb
|
61
|
+
- lib/simple_hl7/composite.rb
|
62
|
+
- lib/simple_hl7/field.rb
|
63
|
+
- lib/simple_hl7/message.rb
|
64
|
+
- lib/simple_hl7/msh_segment.rb
|
65
|
+
- lib/simple_hl7/segment.rb
|
66
|
+
- lib/simple_hl7/separator_characters.rb
|
67
|
+
- lib/simple_hl7/subcomponent.rb
|
68
|
+
- lib/simple_hl7/version.rb
|
69
|
+
- simple_hl7.gemspec
|
70
|
+
- spec/simple_hl7/component_container_spec.rb
|
71
|
+
- spec/simple_hl7/component_spec.rb
|
72
|
+
- spec/simple_hl7/composite_spec.rb
|
73
|
+
- spec/simple_hl7/field_spec.rb
|
74
|
+
- spec/simple_hl7/message_spec.rb
|
75
|
+
- spec/simple_hl7/msh_segment_spec.rb
|
76
|
+
- spec/simple_hl7/segment_spec.rb
|
77
|
+
- spec/simple_hl7/subcomponent_spec.rb
|
78
|
+
homepage: https://github.com/alivecor/simple_hl7
|
79
|
+
licenses: []
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.8.24
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Parse and generate hl7 messages
|
102
|
+
test_files:
|
103
|
+
- spec/simple_hl7/component_container_spec.rb
|
104
|
+
- spec/simple_hl7/component_spec.rb
|
105
|
+
- spec/simple_hl7/composite_spec.rb
|
106
|
+
- spec/simple_hl7/field_spec.rb
|
107
|
+
- spec/simple_hl7/message_spec.rb
|
108
|
+
- spec/simple_hl7/msh_segment_spec.rb
|
109
|
+
- spec/simple_hl7/segment_spec.rb
|
110
|
+
- spec/simple_hl7/subcomponent_spec.rb
|