pipehat 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +18 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +184 -0
- data/Rakefile +10 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/lib/pipehat.rb +22 -0
- data/lib/pipehat/component/base.rb +30 -0
- data/lib/pipehat/define_type.rb +68 -0
- data/lib/pipehat/field/base.rb +59 -0
- data/lib/pipehat/message.rb +32 -0
- data/lib/pipehat/node.rb +53 -0
- data/lib/pipehat/parser.rb +149 -0
- data/lib/pipehat/repeat/base.rb +31 -0
- data/lib/pipehat/segment/aip.rb +21 -0
- data/lib/pipehat/segment/ais.rb +21 -0
- data/lib/pipehat/segment/al1.rb +15 -0
- data/lib/pipehat/segment/base.rb +124 -0
- data/lib/pipehat/segment/dg1.rb +30 -0
- data/lib/pipehat/segment/err.rb +21 -0
- data/lib/pipehat/segment/evn.rb +16 -0
- data/lib/pipehat/segment/in1.rb +64 -0
- data/lib/pipehat/segment/msa.rb +17 -0
- data/lib/pipehat/segment/msh.rb +52 -0
- data/lib/pipehat/segment/obr.rb +63 -0
- data/lib/pipehat/segment/obx.rb +37 -0
- data/lib/pipehat/segment/orc.rb +43 -0
- data/lib/pipehat/segment/pid.rb +49 -0
- data/lib/pipehat/segment/prd.rb +23 -0
- data/lib/pipehat/segment/pv1.rb +63 -0
- data/lib/pipehat/segment/rf1.rb +34 -0
- data/lib/pipehat/segment/rgs.rb +12 -0
- data/lib/pipehat/segment/sch.rb +36 -0
- data/lib/pipehat/subcomponent/base.rb +27 -0
- data/lib/pipehat/types/aui.rb +8 -0
- data/lib/pipehat/types/ce.rb +11 -0
- data/lib/pipehat/types/cne.rb +27 -0
- data/lib/pipehat/types/cnn.rb +16 -0
- data/lib/pipehat/types/cp.rb +11 -0
- data/lib/pipehat/types/cq.rb +7 -0
- data/lib/pipehat/types/cwe.rb +27 -0
- data/lib/pipehat/types/cx.rb +17 -0
- data/lib/pipehat/types/dld.rb +7 -0
- data/lib/pipehat/types/dt.rb +4 -0
- data/lib/pipehat/types/dtm.rb +4 -0
- data/lib/pipehat/types/ei.rb +9 -0
- data/lib/pipehat/types/eip.rb +7 -0
- data/lib/pipehat/types/erl.rb +11 -0
- data/lib/pipehat/types/fn.rb +10 -0
- data/lib/pipehat/types/hd.rb +8 -0
- data/lib/pipehat/types/id.rb +4 -0
- data/lib/pipehat/types/is.rb +4 -0
- data/lib/pipehat/types/mo.rb +7 -0
- data/lib/pipehat/types/moc.rb +7 -0
- data/lib/pipehat/types/msg.rb +8 -0
- data/lib/pipehat/types/ndl.rb +16 -0
- data/lib/pipehat/types/nm.rb +4 -0
- data/lib/pipehat/types/pl.rb +16 -0
- data/lib/pipehat/types/pln.rb +9 -0
- data/lib/pipehat/types/prl.rb +8 -0
- data/lib/pipehat/types/pt.rb +7 -0
- data/lib/pipehat/types/rc.rb +7 -0
- data/lib/pipehat/types/sad.rb +8 -0
- data/lib/pipehat/types/si.rb +4 -0
- data/lib/pipehat/types/snm.rb +4 -0
- data/lib/pipehat/types/st.rb +4 -0
- data/lib/pipehat/types/ts.rb +7 -0
- data/lib/pipehat/types/tx.rb +4 -0
- data/lib/pipehat/types/varies.rb +28 -0
- data/lib/pipehat/types/vid.rb +8 -0
- data/lib/pipehat/types/xad.rb +28 -0
- data/lib/pipehat/types/xcn.rb +30 -0
- data/lib/pipehat/types/xon.rb +15 -0
- data/lib/pipehat/types/xpn.rb +20 -0
- data/lib/pipehat/types/xtn.rb +22 -0
- data/lib/pipehat/version.rb +3 -0
- data/pipehat.gemspec +27 -0
- metadata +127 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pipehat
|
4
|
+
module Field
|
5
|
+
class Base < Pipehat::Node
|
6
|
+
def initialize(segment, fnum)
|
7
|
+
@segment = segment
|
8
|
+
@fnum = fnum
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :segment, :fnum
|
12
|
+
|
13
|
+
def repeat(rnum)
|
14
|
+
repeat_class.new(segment, fnum, rnum)
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](rnum)
|
18
|
+
repeat(rnum)
|
19
|
+
end
|
20
|
+
|
21
|
+
def []=(rnum, value)
|
22
|
+
repeat(rnum).set(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def first
|
26
|
+
repeat(1)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Set on a field should replace the entire tree at this field
|
30
|
+
# This should discard anything under repeats etc
|
31
|
+
def set(value)
|
32
|
+
segment.set_field(fnum, value)
|
33
|
+
end
|
34
|
+
|
35
|
+
# calling component(n) on a field assumes you mean the first repeat
|
36
|
+
def component(cnum, type = Pipehat::Component::Base)
|
37
|
+
first.component(cnum, type)
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_hl7
|
41
|
+
(segment.tree(fnum) || []).map do |repeat|
|
42
|
+
(repeat || []).map do |component|
|
43
|
+
(component || []).join(parser.subcomponent_sep)
|
44
|
+
end.join(parser.component_sep)
|
45
|
+
end.join(parser.repetition_sep)
|
46
|
+
end
|
47
|
+
|
48
|
+
def inspect
|
49
|
+
inspect_node(fnum)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def repeat_class
|
55
|
+
Object.const_get(self.class.name.sub(/Field/, "Repeat"))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pipehat
|
4
|
+
class Message
|
5
|
+
def initialize(hl7 = "", parser: Pipehat::DEFAULT_PARSER)
|
6
|
+
@parser = parser
|
7
|
+
@segments = parser.parse(hl7)
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :parser
|
11
|
+
|
12
|
+
def to_hl7
|
13
|
+
segments.map(&:to_hl7).join(parser.segment_sep)
|
14
|
+
end
|
15
|
+
|
16
|
+
def <<(segment)
|
17
|
+
@segments << segment
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns an enumerator over the message's segments
|
21
|
+
# The optional parameter limits it to the given type
|
22
|
+
def segments(type = nil)
|
23
|
+
return to_enum(:segments, type) unless block_given?
|
24
|
+
|
25
|
+
@segments.each do |segment|
|
26
|
+
next if type && segment.segment_name != type.to_s
|
27
|
+
|
28
|
+
yield segment
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/pipehat/node.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pipehat
|
4
|
+
# Parent class of all accessor types (Field, Repeat, (Sub)Component
|
5
|
+
# providing common methods
|
6
|
+
class Node
|
7
|
+
def fnum
|
8
|
+
1
|
9
|
+
end
|
10
|
+
|
11
|
+
def rnum
|
12
|
+
1
|
13
|
+
end
|
14
|
+
|
15
|
+
def cnum
|
16
|
+
1
|
17
|
+
end
|
18
|
+
|
19
|
+
def snum
|
20
|
+
1
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
segment.parser.unescape(unescaped)
|
25
|
+
end
|
26
|
+
|
27
|
+
def unescaped
|
28
|
+
segment.get(fnum, rnum, cnum, snum)
|
29
|
+
end
|
30
|
+
|
31
|
+
def component_names
|
32
|
+
self.class.component_names
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.component_names
|
36
|
+
@component_names ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
def parser
|
40
|
+
segment.parser
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def inspect_node(*nums)
|
46
|
+
s = "#<#{self.class} #{segment.segment_name}(#{nums.join(",")}) "
|
47
|
+
maxlen = 76 - s.length
|
48
|
+
fragment = to_hl7
|
49
|
+
fragment = fragment[0, maxlen - 3] + "..." if fragment.length > maxlen
|
50
|
+
"#{s}#{fragment}>"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "strscan"
|
4
|
+
|
5
|
+
module Pipehat
|
6
|
+
class Parser
|
7
|
+
def segment_sep
|
8
|
+
"\r"
|
9
|
+
end
|
10
|
+
|
11
|
+
def field_sep
|
12
|
+
"|"
|
13
|
+
end
|
14
|
+
|
15
|
+
def repetition_sep
|
16
|
+
"~"
|
17
|
+
end
|
18
|
+
|
19
|
+
def component_sep
|
20
|
+
"^"
|
21
|
+
end
|
22
|
+
|
23
|
+
def subcomponent_sep
|
24
|
+
"&"
|
25
|
+
end
|
26
|
+
|
27
|
+
def escape_char
|
28
|
+
"\\"
|
29
|
+
end
|
30
|
+
|
31
|
+
def escaped_field_sep
|
32
|
+
"#{escape_char}F#{escape_char}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def escaped_repetition_sep
|
36
|
+
"#{escape_char}R#{escape_char}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def escaped_component_sep
|
40
|
+
"#{escape_char}S#{escape_char}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def escaped_subcomponent_sep
|
44
|
+
"#{escape_char}T#{escape_char}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def escaped_escape_char
|
48
|
+
"#{escape_char}E#{escape_char}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def escaped_field_sep_regex
|
52
|
+
@escaped_field_sep_regex ||= Regexp.new Regexp.quote escaped_field_sep
|
53
|
+
end
|
54
|
+
|
55
|
+
def escaped_repetition_sep_regex
|
56
|
+
@escaped_repetition_sep_regex ||= Regexp.new Regexp.quote escaped_repetition_sep
|
57
|
+
end
|
58
|
+
|
59
|
+
def escaped_component_sep_regex
|
60
|
+
@escaped_component_sep_regex ||= Regexp.new Regexp.quote escaped_component_sep
|
61
|
+
end
|
62
|
+
|
63
|
+
def escaped_subcomponent_sep_regex
|
64
|
+
@escaped_subcomponent_sep_regex ||= Regexp.new Regexp.quote escaped_subcomponent_sep
|
65
|
+
end
|
66
|
+
|
67
|
+
def escaped_escape_char_regex
|
68
|
+
@escaped_escape_char_regex ||= Regexp.new Regexp.quote escaped_escape_char
|
69
|
+
end
|
70
|
+
|
71
|
+
def escaped_hex_regex
|
72
|
+
@escaped_hex_regex ||= Regexp.new "#{Regexp.quote escape_char}X\\h+#{Regexp.quote escape_char}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def msh2
|
76
|
+
@msh2 ||= [
|
77
|
+
component_sep,
|
78
|
+
repetition_sep,
|
79
|
+
escape_char,
|
80
|
+
subcomponent_sep
|
81
|
+
].join
|
82
|
+
end
|
83
|
+
|
84
|
+
def escape(string)
|
85
|
+
return "" if string.nil?
|
86
|
+
|
87
|
+
string.each_char.map do |chr|
|
88
|
+
case chr
|
89
|
+
when field_sep then escaped_field_sep
|
90
|
+
when repetition_sep then escaped_repetition_sep
|
91
|
+
when component_sep then escaped_component_sep
|
92
|
+
when subcomponent_sep then escaped_subcomponent_sep
|
93
|
+
when escape_char then escaped_escape_char
|
94
|
+
else
|
95
|
+
if (32.chr..127.chr).cover?(chr)
|
96
|
+
chr
|
97
|
+
else
|
98
|
+
format("\\X%02X\\", chr.ord)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end.join
|
102
|
+
end
|
103
|
+
|
104
|
+
def unescape(string)
|
105
|
+
return "" if string.nil?
|
106
|
+
return string unless string.include?(escape_char)
|
107
|
+
|
108
|
+
s = StringScanner.new(string)
|
109
|
+
out = String.new
|
110
|
+
until s.eos?
|
111
|
+
if s.scan(escaped_field_sep_regex)
|
112
|
+
out << field_sep
|
113
|
+
elsif s.scan(escaped_repetition_sep_regex)
|
114
|
+
out << repetition_sep
|
115
|
+
elsif s.scan(escaped_component_sep_regex)
|
116
|
+
out << component_sep
|
117
|
+
elsif s.scan(escaped_subcomponent_sep_regex)
|
118
|
+
out << subcomponent_sep
|
119
|
+
elsif s.scan(escaped_escape_char_regex)
|
120
|
+
out << escape_char
|
121
|
+
elsif s.scan(escaped_hex_regex)
|
122
|
+
s.matched[2..-2].scan(/../).each { |hex| out << hex.to_i(16).chr }
|
123
|
+
else
|
124
|
+
out << s.getch
|
125
|
+
end
|
126
|
+
end
|
127
|
+
out
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns an array of Pipehat::Segments of the right class
|
131
|
+
def parse(hl7)
|
132
|
+
hl7.split(segment_sep).map do |seg|
|
133
|
+
name = seg[0, seg.index("|")]
|
134
|
+
klass = if Pipehat::Segment.const_defined?(name)
|
135
|
+
Pipehat::Segment.const_get(name)
|
136
|
+
else
|
137
|
+
Pipehat::Segment::Base
|
138
|
+
end
|
139
|
+
klass.new(seg, parser: self)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def inspect
|
144
|
+
"#<#{self.class} #{field_sep}#{msh2}>"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
DEFAULT_PARSER = Parser.new
|
149
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Pipehat
|
2
|
+
module Repeat
|
3
|
+
class Base < Pipehat::Node
|
4
|
+
def initialize(segment, fnum, rnum)
|
5
|
+
@segment = segment
|
6
|
+
@fnum = fnum
|
7
|
+
@rnum = rnum
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :segment, :fnum, :rnum
|
11
|
+
|
12
|
+
def component(cnum, type = Pipehat::Component::Base)
|
13
|
+
type.new(segment, fnum, rnum, cnum)
|
14
|
+
end
|
15
|
+
|
16
|
+
def set(value)
|
17
|
+
segment.set_repeat(fnum, rnum, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hl7
|
21
|
+
(segment.tree(fnum, rnum) || []).map do |component|
|
22
|
+
(component || []).join(parser.subcomponent_sep)
|
23
|
+
end.join(parser.component_sep)
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
inspect_node(fnum, rnum)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pipehat
|
4
|
+
module Segment
|
5
|
+
# Appointment Information - Personnel Resource
|
6
|
+
class AIP < Base
|
7
|
+
add_field :set_id, :SI
|
8
|
+
add_field :segment_action_code, :ID
|
9
|
+
add_field :personnel_resource_id, :XCN
|
10
|
+
add_field :resource_type, :CWE
|
11
|
+
add_field :resource_group, :CWE
|
12
|
+
add_field :start_date_time, :DTM
|
13
|
+
add_field :start_date_time_offset, :NM
|
14
|
+
add_field :start_date_time_offset_units, :CNE
|
15
|
+
add_field :duration, :NM
|
16
|
+
add_field :duration_units, :CNE
|
17
|
+
add_field :allow_substitution_code, :CWE
|
18
|
+
add_field :filler_status_code, :CWE
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pipehat
|
4
|
+
module Segment
|
5
|
+
# Appointment Information
|
6
|
+
class AIS < Base
|
7
|
+
add_field :set_id, :SI
|
8
|
+
add_field :segment_action_code, :ID
|
9
|
+
add_field :universal_service_identifier, :CWE
|
10
|
+
add_field :start_date_time, :DTM
|
11
|
+
add_field :start_date_time_offset, :NM
|
12
|
+
add_field :start_date_time_offset_units, :CNE
|
13
|
+
add_field :duration, :NM
|
14
|
+
add_field :duration_units, :CNE
|
15
|
+
add_field :allow_substitution_code, :CWE
|
16
|
+
add_field :filler_status_code, :CWE
|
17
|
+
add_field :placer_supplemental_service_information, :CWE
|
18
|
+
add_field :filler_supplemental_service_information, :CWE
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pipehat
|
4
|
+
module Segment
|
5
|
+
# Patient Allergy Information
|
6
|
+
class AL1 < Base
|
7
|
+
add_field :set_id, :SI
|
8
|
+
add_field :allergen_type_code, :CE
|
9
|
+
add_field :allergen_code, :CE
|
10
|
+
add_field :allergy_severity_code, :CE
|
11
|
+
add_field :allergy_reaction_code, :ST
|
12
|
+
add_field :identification_date, :DT
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pipehat
|
4
|
+
module Segment
|
5
|
+
class Base
|
6
|
+
def initialize(string = nil, parser: Pipehat::DEFAULT_PARSER)
|
7
|
+
string ||= self.class.name.split("::").last
|
8
|
+
# storage is nested array (to depth of subcomponents). The unescaped
|
9
|
+
# strings is stored (escape/unescape occurs in accessors).
|
10
|
+
@data = string.split(parser.field_sep).map do |field|
|
11
|
+
field.split(parser.repetition_sep).map do |repeat|
|
12
|
+
repeat.split(parser.component_sep).map do |component|
|
13
|
+
component.split(parser.subcomponent_sep)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
@parser = parser
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns a reference to a field by number
|
21
|
+
def field(fnum, type = Pipehat::Field::Base)
|
22
|
+
type.new(self, fnum)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return the raw (unescaped) string at the specified position.
|
26
|
+
# If the position doesn't exist, returns nil.
|
27
|
+
#
|
28
|
+
# Note this works by assuming @data always has arrays nesting to
|
29
|
+
# the subcomponent position
|
30
|
+
def get(fnum, rnum, cnum, snum)
|
31
|
+
tree(fnum, rnum, cnum, snum)
|
32
|
+
end
|
33
|
+
|
34
|
+
def tree(fnum, rnum = nil, cnum = nil, snum = nil)
|
35
|
+
tmp = @data.dig(fnum)
|
36
|
+
tmp = tmp&.dig(rnum - 1) if rnum
|
37
|
+
tmp = tmp&.dig(cnum - 1) if cnum
|
38
|
+
tmp = tmp&.dig(snum - 1) if snum
|
39
|
+
tmp
|
40
|
+
end
|
41
|
+
|
42
|
+
# TODO: set_* currently assume a string only. Would be good to accept
|
43
|
+
# and insert arrays (for nodes higher than subcomponents)
|
44
|
+
# There should also be a way to avoid the escaping (eg, FT values use
|
45
|
+
# escape sequences that shouldn't be escaped again).
|
46
|
+
|
47
|
+
def set_field(fnum, value)
|
48
|
+
@data[fnum] = [[[parser.escape(value)]]]
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_repeat(fnum, rnum, value)
|
52
|
+
@data[fnum] ||= [[[]]]
|
53
|
+
@data[fnum][rnum - 1] = [[parser.escape(value)]]
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_component(fnum, rnum, cnum, value)
|
57
|
+
@data[fnum] ||= [[[]]]
|
58
|
+
@data[fnum][rnum - 1] ||= [[]]
|
59
|
+
@data[fnum][rnum - 1][cnum - 1] = [parser.escape(value)]
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_subcomponent(fnum, rnum, cnum, snum, value)
|
63
|
+
@data[fnum] ||= [[[]]]
|
64
|
+
@data[fnum][rnum - 1] ||= [[]]
|
65
|
+
@data[fnum][rnum - 1][cnum - 1] ||= []
|
66
|
+
@data[fnum][rnum - 1][cnum - 1][snum - 1] = parser.escape(value)
|
67
|
+
end
|
68
|
+
|
69
|
+
def field_names
|
70
|
+
self.class.field_names
|
71
|
+
end
|
72
|
+
|
73
|
+
def segment_name
|
74
|
+
field(0).to_s
|
75
|
+
end
|
76
|
+
|
77
|
+
attr_accessor :parser
|
78
|
+
|
79
|
+
def to_hl7
|
80
|
+
@data.map do |field|
|
81
|
+
(field || []).map do |repeat|
|
82
|
+
(repeat || []).map do |component|
|
83
|
+
(component || []).join(parser.subcomponent_sep)
|
84
|
+
end.join(parser.component_sep)
|
85
|
+
end.join(parser.repetition_sep)
|
86
|
+
end.join(parser.field_sep)
|
87
|
+
end
|
88
|
+
|
89
|
+
class << self
|
90
|
+
# returns a list of the fields define on this segment as symbols
|
91
|
+
def field_names
|
92
|
+
@field_names ||= []
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_field(name, type, options = {})
|
96
|
+
field_names << name
|
97
|
+
count = field_names.size
|
98
|
+
klass = Object.const_get("Pipehat::Field::#{type}")
|
99
|
+
|
100
|
+
invalid_options = options.keys - %i[setter]
|
101
|
+
raise "Invalid options: #{invalid_options.join(", ")}" if invalid_options.any?
|
102
|
+
|
103
|
+
define_method name do
|
104
|
+
field(count, klass)
|
105
|
+
end
|
106
|
+
|
107
|
+
return unless options.fetch(:setter, true)
|
108
|
+
|
109
|
+
define_method "#{name}=" do |value|
|
110
|
+
send(name).set(value)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def inspect
|
116
|
+
s = "#<#{self.class} "
|
117
|
+
maxlen = 76 - s.length
|
118
|
+
fragment = to_hl7
|
119
|
+
fragment = fragment[0, maxlen - 3] + "..." if fragment.length > maxlen
|
120
|
+
"#{s}#{fragment}>"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|