pipehat 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|