tediparse 2.0.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/CHANGELOG.md +319 -0
- data/LICENSE +26 -0
- data/README.md +308 -0
- data/Rakefile +33 -0
- data/bin/tediparse +102 -0
- data/doc/Defining.md +97 -0
- data/doc/Generating-Grammars.md +241 -0
- data/doc/Generating.md +332 -0
- data/doc/Navigating.md +670 -0
- data/doc/Parsing.md +133 -0
- data/doc/README.md +51 -0
- data/doc/Serializing.md +100 -0
- data/doc/Tokenizing.md +136 -0
- data/doc/Validating.md +140 -0
- data/lib/ruby/array.rb +226 -0
- data/lib/ruby/blank.rb +52 -0
- data/lib/ruby/exception.rb +14 -0
- data/lib/ruby/hash.rb +14 -0
- data/lib/ruby/module.rb +60 -0
- data/lib/ruby/object.rb +56 -0
- data/lib/ruby/string.rb +89 -0
- data/lib/ruby/to_d.rb +82 -0
- data/lib/ruby/to_date.rb +26 -0
- data/lib/ruby/to_time.rb +21 -0
- data/lib/ruby/try.rb +46 -0
- data/lib/stupidedi/builder.rb +6 -0
- data/lib/stupidedi/color.rb +94 -0
- data/lib/stupidedi/config/code_list_config.rb +45 -0
- data/lib/stupidedi/config/functional_group_config.rb +72 -0
- data/lib/stupidedi/config/interchange_config.rb +86 -0
- data/lib/stupidedi/config/transaction_set_config.rb +73 -0
- data/lib/stupidedi/config.rb +100 -0
- data/lib/stupidedi/either.rb +286 -0
- data/lib/stupidedi/exceptions/invalid_element_error.rb +7 -0
- data/lib/stupidedi/exceptions/invalid_schema_error.rb +7 -0
- data/lib/stupidedi/exceptions/missing_grammar_error.rb +50 -0
- data/lib/stupidedi/exceptions/output_error.rb +7 -0
- data/lib/stupidedi/exceptions/parse_error.rb +7 -0
- data/lib/stupidedi/exceptions/stupidedi_error.rb +7 -0
- data/lib/stupidedi/exceptions/tokenize_error.rb +7 -0
- data/lib/stupidedi/exceptions/zipper_error.rb +7 -0
- data/lib/stupidedi/exceptions.rb +13 -0
- data/lib/stupidedi/inspect.rb +26 -0
- data/lib/stupidedi/interchanges/element_types/separator_val.rb +79 -0
- data/lib/stupidedi/interchanges/element_types/special_val.rb +48 -0
- data/lib/stupidedi/interchanges/element_types.rb +11 -0
- data/lib/stupidedi/interchanges.rb +21 -0
- data/lib/stupidedi/parser/builder_dsl.rb +292 -0
- data/lib/stupidedi/parser/constraint_table.rb +566 -0
- data/lib/stupidedi/parser/generation.rb +147 -0
- data/lib/stupidedi/parser/identifier_stack.rb +248 -0
- data/lib/stupidedi/parser/instruction.rb +112 -0
- data/lib/stupidedi/parser/instruction_table.rb +222 -0
- data/lib/stupidedi/parser/navigation.rb +786 -0
- data/lib/stupidedi/parser/state_machine.rb +62 -0
- data/lib/stupidedi/parser/states/abstract_state.rb +385 -0
- data/lib/stupidedi/parser/states/failure_state.rb +69 -0
- data/lib/stupidedi/parser/states/functional_group_state.rb +101 -0
- data/lib/stupidedi/parser/states/initial_state.rb +62 -0
- data/lib/stupidedi/parser/states/interchange_state.rb +96 -0
- data/lib/stupidedi/parser/states/loop_state.rb +74 -0
- data/lib/stupidedi/parser/states/table_state.rb +90 -0
- data/lib/stupidedi/parser/states/transaction_set_state.rb +120 -0
- data/lib/stupidedi/parser/states/transmission_state.rb +59 -0
- data/lib/stupidedi/parser/tokenization.rb +195 -0
- data/lib/stupidedi/parser.rb +32 -0
- data/lib/stupidedi/reader/input/abstract_input.rb +137 -0
- data/lib/stupidedi/reader/input/delegated_input.rb +112 -0
- data/lib/stupidedi/reader/input/file_input.rb +157 -0
- data/lib/stupidedi/reader/input.rb +31 -0
- data/lib/stupidedi/reader/position.rb +78 -0
- data/lib/stupidedi/reader/result.rb +172 -0
- data/lib/stupidedi/reader/segment_dict.rb +176 -0
- data/lib/stupidedi/reader/separators.rb +90 -0
- data/lib/stupidedi/reader/stream_reader.rb +173 -0
- data/lib/stupidedi/reader/token_reader.rb +465 -0
- data/lib/stupidedi/reader/tokens/component_element_tok.rb +71 -0
- data/lib/stupidedi/reader/tokens/composite_element_tok.rb +85 -0
- data/lib/stupidedi/reader/tokens/repeated_element_tok.rb +74 -0
- data/lib/stupidedi/reader/tokens/segment_tok.rb +74 -0
- data/lib/stupidedi/reader/tokens/simple_element_tok.rb +76 -0
- data/lib/stupidedi/reader.rb +121 -0
- data/lib/stupidedi/schema/abstract_def.rb +76 -0
- data/lib/stupidedi/schema/abstract_element_def.rb +35 -0
- data/lib/stupidedi/schema/abstract_element_use.rb +47 -0
- data/lib/stupidedi/schema/abstract_use.rb +79 -0
- data/lib/stupidedi/schema/code_list.rb +99 -0
- data/lib/stupidedi/schema/component_element_use.rb +76 -0
- data/lib/stupidedi/schema/composite_element_def.rb +103 -0
- data/lib/stupidedi/schema/composite_element_use.rb +78 -0
- data/lib/stupidedi/schema/element_req.rb +57 -0
- data/lib/stupidedi/schema/functional_group_def.rb +124 -0
- data/lib/stupidedi/schema/generation/definition_generator.rb +139 -0
- data/lib/stupidedi/schema/generation/element_generator.rb +221 -0
- data/lib/stupidedi/schema/generation/flat_file_reader.rb +551 -0
- data/lib/stupidedi/schema/generation/functional_group_generator.rb +64 -0
- data/lib/stupidedi/schema/generation/interchange_generator.rb +145 -0
- data/lib/stupidedi/schema/generation/master_loader_generator.rb +121 -0
- data/lib/stupidedi/schema/generation/models.rb +85 -0
- data/lib/stupidedi/schema/generation/module_loader_generator.rb +64 -0
- data/lib/stupidedi/schema/generation/registration_generator.rb +230 -0
- data/lib/stupidedi/schema/generation/runner.rb +161 -0
- data/lib/stupidedi/schema/generation/segment_generator.rb +130 -0
- data/lib/stupidedi/schema/generation/support.rb +78 -0
- data/lib/stupidedi/schema/generation/support_modules_generator.rb +126 -0
- data/lib/stupidedi/schema/generation/version_modules.rb +35 -0
- data/lib/stupidedi/schema/generation.rb +94 -0
- data/lib/stupidedi/schema/interchange_def.rb +103 -0
- data/lib/stupidedi/schema/loop_def.rb +156 -0
- data/lib/stupidedi/schema/repeat_count.rb +86 -0
- data/lib/stupidedi/schema/segment_def.rb +122 -0
- data/lib/stupidedi/schema/segment_req.rb +46 -0
- data/lib/stupidedi/schema/segment_use.rb +99 -0
- data/lib/stupidedi/schema/simple_element_def.rb +51 -0
- data/lib/stupidedi/schema/simple_element_use.rb +83 -0
- data/lib/stupidedi/schema/syntax_note.rb +52 -0
- data/lib/stupidedi/schema/table_def.rb +178 -0
- data/lib/stupidedi/schema/transaction_set_def.rb +125 -0
- data/lib/stupidedi/schema.rb +30 -0
- data/lib/stupidedi/sets.rb +42 -0
- data/lib/stupidedi/transaction_sets/builder/dsl.rb +192 -0
- data/lib/stupidedi/transaction_sets/builder.rb +188 -0
- data/lib/stupidedi/transaction_sets/common/implementations/element_reqs.rb +37 -0
- data/lib/stupidedi/transaction_sets/common/implementations/segment_reqs.rb +31 -0
- data/lib/stupidedi/transaction_sets/common/implementations.rb +11 -0
- data/lib/stupidedi/transaction_sets/common.rb +8 -0
- data/lib/stupidedi/transaction_sets/validation/ambiguity.rb +395 -0
- data/lib/stupidedi/transaction_sets/validation/implementation.rb +12 -0
- data/lib/stupidedi/transaction_sets/validation.rb +9 -0
- data/lib/stupidedi/transaction_sets.rb +25 -0
- data/lib/stupidedi/values/abstract_element_val.rb +19 -0
- data/lib/stupidedi/values/abstract_val.rb +133 -0
- data/lib/stupidedi/values/composite_element_val.rb +102 -0
- data/lib/stupidedi/values/functional_group_val.rb +105 -0
- data/lib/stupidedi/values/interchange_val.rb +102 -0
- data/lib/stupidedi/values/invalid_envelope_val.rb +61 -0
- data/lib/stupidedi/values/invalid_segment_val.rb +89 -0
- data/lib/stupidedi/values/loop_val.rb +73 -0
- data/lib/stupidedi/values/repeated_element_val.rb +113 -0
- data/lib/stupidedi/values/segment_val.rb +105 -0
- data/lib/stupidedi/values/segment_val_group.rb +20 -0
- data/lib/stupidedi/values/simple_element_val.rb +80 -0
- data/lib/stupidedi/values/table_val.rb +69 -0
- data/lib/stupidedi/values/transaction_set_val.rb +69 -0
- data/lib/stupidedi/values/transmission_val.rb +56 -0
- data/lib/stupidedi/values.rb +22 -0
- data/lib/stupidedi/version.rb +4 -0
- data/lib/stupidedi/versions/common/element_reqs.rb +13 -0
- data/lib/stupidedi/versions/common/element_types/an.rb +386 -0
- data/lib/stupidedi/versions/common/element_types/dt.rb +572 -0
- data/lib/stupidedi/versions/common/element_types/id.rb +304 -0
- data/lib/stupidedi/versions/common/element_types/nn.rb +312 -0
- data/lib/stupidedi/versions/common/element_types/operators.rb +128 -0
- data/lib/stupidedi/versions/common/element_types/r.rb +342 -0
- data/lib/stupidedi/versions/common/element_types/simple_element.rb +73 -0
- data/lib/stupidedi/versions/common/element_types/tm.rb +347 -0
- data/lib/stupidedi/versions/common/element_types.rb +29 -0
- data/lib/stupidedi/versions/common/segment_reqs.rb +15 -0
- data/lib/stupidedi/versions/common/syntax_notes.rb +172 -0
- data/lib/stupidedi/versions/common.rb +11 -0
- data/lib/stupidedi/versions.rb +25 -0
- data/lib/stupidedi/writer/claredi.rb +178 -0
- data/lib/stupidedi/writer/default.rb +119 -0
- data/lib/stupidedi/writer.rb +7 -0
- data/lib/stupidedi/zipper/abstract_cursor.rb +351 -0
- data/lib/stupidedi/zipper/dangling_cursor.rb +103 -0
- data/lib/stupidedi/zipper/edited_cursor.rb +157 -0
- data/lib/stupidedi/zipper/memoized_cursor.rb +133 -0
- data/lib/stupidedi/zipper/path.rb +132 -0
- data/lib/stupidedi/zipper/root_cursor.rb +120 -0
- data/lib/stupidedi/zipper/stack_cursor.rb +107 -0
- data/lib/stupidedi/zipper.rb +45 -0
- data/lib/stupidedi.rb +69 -0
- data/lib/tediparse.rb +1 -0
- metadata +249 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Schema
|
|
6
|
+
#
|
|
7
|
+
# @see X12-5.pdf 3.2.1 Basic Interchange Service Request
|
|
8
|
+
# @see X222.pdf B.1.1.4.1 Interchange Control Structures
|
|
9
|
+
#
|
|
10
|
+
class InterchangeDef < AbstractDef
|
|
11
|
+
include Inspect
|
|
12
|
+
|
|
13
|
+
# @return [String]
|
|
14
|
+
attr_reader :id
|
|
15
|
+
|
|
16
|
+
# @return [Array<SegmentUse>]
|
|
17
|
+
attr_reader :header_segment_uses
|
|
18
|
+
|
|
19
|
+
# @return [Array<SegmentUse>]
|
|
20
|
+
attr_reader :trailer_segment_uses
|
|
21
|
+
|
|
22
|
+
# @return [Values::InterchangeVal]
|
|
23
|
+
abstract :empty, :args => %w(separators)
|
|
24
|
+
|
|
25
|
+
# @return [Reader::Separators]
|
|
26
|
+
abstract :separators, :args => %w(isa_segment_val)
|
|
27
|
+
|
|
28
|
+
def initialize(id, header_segment_uses, trailer_segment_uses)
|
|
29
|
+
@id, @header_segment_uses, @trailer_segment_uses =
|
|
30
|
+
id, header_segment_uses, trailer_segment_uses
|
|
31
|
+
|
|
32
|
+
@header_segment_uses = header_segment_uses.map{|x| x.copy(:parent => self) }
|
|
33
|
+
@trailer_segment_uses = trailer_segment_uses.map{|x| x.copy(:parent => self) }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [InterchangeDef]
|
|
37
|
+
def copy(changes = {})
|
|
38
|
+
InterchangeDef.new \
|
|
39
|
+
changes.fetch(:id, @id),
|
|
40
|
+
changes.fetch(:header_segment_uses, @header_segment_uses),
|
|
41
|
+
changes.fetch(:trailer_segment_uses, @trailer_segment_uses)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def required?
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [String]
|
|
49
|
+
def descriptor
|
|
50
|
+
"interchange #{id}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @return [SegmentUse]
|
|
54
|
+
def entry_segment_use
|
|
55
|
+
@header_segment_uses.head
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# @return [Array<SegmentUse>]
|
|
59
|
+
def children
|
|
60
|
+
@header_segment_uses + @trailer_segment_uses
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# (see AbstractDef)
|
|
64
|
+
def interchange?
|
|
65
|
+
true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @return [AbstractSet<CodeList>]
|
|
69
|
+
def code_lists
|
|
70
|
+
children.map(&:code_lists).inject(&:|)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# @return [void]
|
|
74
|
+
def pretty_print(q)
|
|
75
|
+
q.text("InterchangeDef[#{id}]")
|
|
76
|
+
q.group(2, "(", ")") do
|
|
77
|
+
q.breakable ""
|
|
78
|
+
@header_segment_uses.each do |e|
|
|
79
|
+
unless q.current_group.first?
|
|
80
|
+
q.text ","
|
|
81
|
+
q.breakable
|
|
82
|
+
end
|
|
83
|
+
q.pp e
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
unless q.current_group.first?
|
|
87
|
+
q.text ","
|
|
88
|
+
q.breakable
|
|
89
|
+
end
|
|
90
|
+
q.text "... (FUNCTIONAL GROUPS) ..."
|
|
91
|
+
|
|
92
|
+
@trailer_segment_uses.each do |e|
|
|
93
|
+
unless q.current_group.first?
|
|
94
|
+
q.text ","
|
|
95
|
+
q.breakable
|
|
96
|
+
end
|
|
97
|
+
q.pp e
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Schema
|
|
6
|
+
#
|
|
7
|
+
# @see X222.pdf 2.2.2 Loops
|
|
8
|
+
# @see X222.pdf B.1.3.12.4 Loops of Data Segments
|
|
9
|
+
#
|
|
10
|
+
class LoopDef < AbstractDef
|
|
11
|
+
include Inspect
|
|
12
|
+
|
|
13
|
+
# @return [String]
|
|
14
|
+
attr_reader :id
|
|
15
|
+
|
|
16
|
+
# @return [RepeatCount]
|
|
17
|
+
attr_reader :repeat_count
|
|
18
|
+
|
|
19
|
+
# @return [Array<SegmentUse, LoopDef>]
|
|
20
|
+
attr_reader :children
|
|
21
|
+
|
|
22
|
+
# @return [LoopDef, TableDef]
|
|
23
|
+
attr_reader :parent
|
|
24
|
+
|
|
25
|
+
def_delegators :entry_segment_use, :requirement
|
|
26
|
+
|
|
27
|
+
def_delegators :requirement, :required?, :optional?
|
|
28
|
+
|
|
29
|
+
def initialize(id, repeat_count, children, parent)
|
|
30
|
+
@id, @repeat_count, @children, @parent =
|
|
31
|
+
id, repeat_count, children, parent
|
|
32
|
+
|
|
33
|
+
# Delay re-parenting until the entire definition tree has a root
|
|
34
|
+
# to prevent unnecessarily copying objects
|
|
35
|
+
unless parent.nil?
|
|
36
|
+
@children = @children.map{|x| x.copy(:parent => self) }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [LoopDef]
|
|
41
|
+
def copy(changes = {})
|
|
42
|
+
LoopDef.new \
|
|
43
|
+
changes.fetch(:id, @id),
|
|
44
|
+
changes.fetch(:repeat_count, @repeat_count),
|
|
45
|
+
changes.fetch(:children, @children),
|
|
46
|
+
changes.fetch(:parent, @parent)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Returns segments before the first LoopDef.
|
|
50
|
+
# @deprecated Use {#children} instead. This method does not include
|
|
51
|
+
# segments that appear between child loops in interleaved structures.
|
|
52
|
+
# @return [Array<SegmentUse>]
|
|
53
|
+
def header_segment_uses
|
|
54
|
+
@children.take_while{|x| x.is_a?(SegmentUse) }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns all LoopDef children.
|
|
58
|
+
# @deprecated Use {#children} instead and filter with `select{|x| x.is_a?(LoopDef)}`.
|
|
59
|
+
# @return [Array<LoopDef>]
|
|
60
|
+
def loop_defs
|
|
61
|
+
@children.select{|x| x.is_a?(LoopDef) }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Returns segments after the last LoopDef.
|
|
65
|
+
# @deprecated Use {#children} instead. This method does not include
|
|
66
|
+
# segments that appear between child loops in interleaved structures.
|
|
67
|
+
# @return [Array<SegmentUse>]
|
|
68
|
+
def trailer_segment_uses
|
|
69
|
+
last_loop_idx = @children.rindex{|x| x.is_a?(LoopDef) }
|
|
70
|
+
return [] if last_loop_idx.nil?
|
|
71
|
+
@children.drop(last_loop_idx + 1).select{|x| x.is_a?(SegmentUse) }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @return [String]
|
|
75
|
+
def descriptor
|
|
76
|
+
"loop #{id}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def repeatable?
|
|
80
|
+
@repeat_count.try{|r| r.include?(2) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @see X222 B.1.1.3.11.1 Loop Control Segments
|
|
84
|
+
# @see X222 B.1.1.3.12.4 Loops of Data Segments Bounded Loops
|
|
85
|
+
def bounded?
|
|
86
|
+
first_seg = @children.find{|x| x.is_a?(SegmentUse) }
|
|
87
|
+
last_seg = @children.reverse.find{|x| x.is_a?(SegmentUse) }
|
|
88
|
+
first_seg && last_seg &&
|
|
89
|
+
first_seg.definition.id == :LS &&
|
|
90
|
+
last_seg.definition.id == :LE
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# @see X12.59 5.6 HL-initiated Loop
|
|
94
|
+
def hierarchical?
|
|
95
|
+
first_seg = @children.find{|x| x.is_a?(SegmentUse) }
|
|
96
|
+
first_seg && first_seg.definition.id == :HL
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @return [SegmentUse]
|
|
100
|
+
def entry_segment_use
|
|
101
|
+
@children.find{|x| x.is_a?(SegmentUse) }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# @return [LoopVal]
|
|
105
|
+
def empty
|
|
106
|
+
Values::LoopVal.new(self, [])
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def loop?
|
|
110
|
+
true
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @return [AbstractSet<CodeList>]
|
|
114
|
+
def code_lists
|
|
115
|
+
@children.map(&:code_lists).inject(&:|)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @return [void]
|
|
119
|
+
def pretty_print(q)
|
|
120
|
+
q.text("LoopDef[#{@id}]")
|
|
121
|
+
q.group(2, "(", ")") do
|
|
122
|
+
q.breakable ""
|
|
123
|
+
@children.each do |e|
|
|
124
|
+
unless q.current_group.first?
|
|
125
|
+
q.text ","
|
|
126
|
+
q.breakable
|
|
127
|
+
end
|
|
128
|
+
q.pp e
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class << LoopDef
|
|
135
|
+
# @group Constructors
|
|
136
|
+
#########################################################################
|
|
137
|
+
|
|
138
|
+
# @return [LoopDef]
|
|
139
|
+
def build(id, repeat_count, *children)
|
|
140
|
+
# Validate: first child must be a SegmentUse
|
|
141
|
+
if children.empty? || !children.head.is_a?(SegmentUse)
|
|
142
|
+
raise Exceptions::InvalidSchemaError,
|
|
143
|
+
"first child must be a SegmentUse"
|
|
144
|
+
elsif children.head.repeat_count.include?(2)
|
|
145
|
+
raise Exceptions::InvalidSchemaError,
|
|
146
|
+
"first child must have RepeatCount.bounded(1)"
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
new(id, repeat_count, children, nil)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# @endgroup
|
|
153
|
+
#########################################################################
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Schema
|
|
6
|
+
class RepeatCount
|
|
7
|
+
class Bounded < RepeatCount
|
|
8
|
+
include Comparable
|
|
9
|
+
|
|
10
|
+
def_delegators :@max, :<=>
|
|
11
|
+
|
|
12
|
+
# @return [Integer]
|
|
13
|
+
attr_reader :max
|
|
14
|
+
|
|
15
|
+
def initialize(max)
|
|
16
|
+
@max = max
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def include?(n)
|
|
20
|
+
n <= @max
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def exclude?(n)
|
|
24
|
+
n > @max
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def inspect
|
|
28
|
+
@max.to_s
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @private
|
|
33
|
+
Once = Class.new(Bounded) do
|
|
34
|
+
def initialize
|
|
35
|
+
@max = 1
|
|
36
|
+
end
|
|
37
|
+
end.new
|
|
38
|
+
|
|
39
|
+
# @private
|
|
40
|
+
Unbounded = Class.new(RepeatCount) do
|
|
41
|
+
include Comparable
|
|
42
|
+
|
|
43
|
+
Infinity = 1.0/0.0
|
|
44
|
+
|
|
45
|
+
def include?(n)
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def exclude?(n)
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def <=>(n)
|
|
54
|
+
Infinity <=> n
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def inspect
|
|
58
|
+
">1"
|
|
59
|
+
end
|
|
60
|
+
end.new
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class << RepeatCount
|
|
64
|
+
# @group Constructors
|
|
65
|
+
#########################################################################
|
|
66
|
+
|
|
67
|
+
def bounded(n)
|
|
68
|
+
if n < 1
|
|
69
|
+
raise Exceptions::InvalidSchemaError,
|
|
70
|
+
"n must be positive"
|
|
71
|
+
elsif n == 1
|
|
72
|
+
RepeatCount::Once
|
|
73
|
+
else
|
|
74
|
+
RepeatCount::Bounded.new(n)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def unbounded
|
|
79
|
+
RepeatCount::Unbounded
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @endgroup
|
|
83
|
+
#########################################################################
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Schema
|
|
6
|
+
#
|
|
7
|
+
# @see X222.pdf B.1.1.3.4 Data Segment
|
|
8
|
+
#
|
|
9
|
+
class SegmentDef < AbstractDef
|
|
10
|
+
include Inspect
|
|
11
|
+
|
|
12
|
+
# @return [Symbol]
|
|
13
|
+
attr_reader :id
|
|
14
|
+
|
|
15
|
+
# @return [String]
|
|
16
|
+
attr_reader :name
|
|
17
|
+
|
|
18
|
+
# @return [String]
|
|
19
|
+
attr_reader :purpose
|
|
20
|
+
|
|
21
|
+
# @return [Array<SimpleElementUse, CompositeElementUse>]
|
|
22
|
+
attr_reader :element_uses
|
|
23
|
+
|
|
24
|
+
# @return [Array<SyntaxNote>]
|
|
25
|
+
attr_reader :syntax_notes
|
|
26
|
+
|
|
27
|
+
# @return [SegmentUse]
|
|
28
|
+
attr_reader :parent
|
|
29
|
+
|
|
30
|
+
def initialize(id, name, purpose, element_uses, syntax_notes, parent)
|
|
31
|
+
@id, @name, @purpose, @element_uses, @syntax_notes, @parent =
|
|
32
|
+
id, name, purpose.join, element_uses, syntax_notes, parent
|
|
33
|
+
|
|
34
|
+
# Delay re-parenting until the entire definition tree has a root
|
|
35
|
+
# to prevent unnecessarily copying objects
|
|
36
|
+
unless parent.nil?
|
|
37
|
+
@element_uses = @element_uses.map{|x| x.copy(:parent => self) }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [SegmentDef]
|
|
42
|
+
def copy(changes = {})
|
|
43
|
+
SegmentDef.new \
|
|
44
|
+
changes.fetch(:id, @id),
|
|
45
|
+
changes.fetch(:name, @name),
|
|
46
|
+
changes.fetch(:purpose, @purpose),
|
|
47
|
+
changes.fetch(:element_uses, @element_uses),
|
|
48
|
+
changes.fetch(:syntax_notes, @syntax_notes),
|
|
49
|
+
changes.fetch(:parent, @parent)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [String]
|
|
53
|
+
def descriptor
|
|
54
|
+
"segment #{id} #{name}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @return [SegmentUse]
|
|
58
|
+
def use(position, requirement, repeat_count)
|
|
59
|
+
SegmentUse.new(self, position, requirement, repeat_count, nil)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Values::SegmentVal]
|
|
63
|
+
def empty(usage)
|
|
64
|
+
Values::SegmentVal.new([], usage)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def value(element_vals, usage, position)
|
|
68
|
+
Values::SegmentVal.new(element_vals, usage, position)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def segment?
|
|
72
|
+
true
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @return [AbstractSet<CodeList>]
|
|
76
|
+
def code_lists
|
|
77
|
+
@element_uses.map(&:code_lists).inject(&:|)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @return [void]
|
|
81
|
+
def pretty_print(q)
|
|
82
|
+
q.text "SegmentDef[#{@id}]"
|
|
83
|
+
|
|
84
|
+
q.group(2, "(", ")") do
|
|
85
|
+
q.breakable ""
|
|
86
|
+
@element_uses.each do |e|
|
|
87
|
+
unless q.current_group.first?
|
|
88
|
+
q.text ","
|
|
89
|
+
q.breakable
|
|
90
|
+
end
|
|
91
|
+
q.pp e
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class << SegmentDef
|
|
98
|
+
# @group Constructors
|
|
99
|
+
#########################################################################
|
|
100
|
+
|
|
101
|
+
# @return [SegmentDef]
|
|
102
|
+
def build(id, name, purpose, *args)
|
|
103
|
+
element_uses = args.take_while{|x| x.is_a?(AbstractElementUse) }
|
|
104
|
+
syntax_notes = args.drop(element_uses.length)
|
|
105
|
+
|
|
106
|
+
# @todo: Validate SyntaxNotes (beyond this rudimentary validation)
|
|
107
|
+
syntax_notes.each do |sn|
|
|
108
|
+
unless sn.indexes.max - 1 <= element_uses.length
|
|
109
|
+
raise Exceptions::InvalidSchemaError,
|
|
110
|
+
"Syntax note for #{id} (#{element_uses.length} elements) " +
|
|
111
|
+
"refers to non-existent element #{sn.indexes.max}"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
new(id, name, purpose, element_uses, syntax_notes, nil)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @endgroup
|
|
119
|
+
#########################################################################
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Schema
|
|
6
|
+
#
|
|
7
|
+
# The 5010 X12 "data segment requirement designator"s include
|
|
8
|
+
# M - Mandatory
|
|
9
|
+
# O - Optional
|
|
10
|
+
#
|
|
11
|
+
# @see X222 B.1.3.12.6 Data Segment Requirement Designators
|
|
12
|
+
#
|
|
13
|
+
# The HIPAA implementation guides "industry usage" include
|
|
14
|
+
# SITUATIONAL
|
|
15
|
+
# REQUIRED
|
|
16
|
+
#
|
|
17
|
+
class SegmentReq
|
|
18
|
+
def initialize(required, forbidden, to_s)
|
|
19
|
+
@required, @forbidden, @to_s =
|
|
20
|
+
required, forbidden, to_s
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def required?
|
|
24
|
+
@required
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def forbidden?
|
|
28
|
+
@forbidden
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def optional?
|
|
32
|
+
not (@required or @forbidden)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# @return [void]
|
|
36
|
+
def pretty_print(q)
|
|
37
|
+
q.text @to_s
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @return [String]
|
|
41
|
+
def inspect
|
|
42
|
+
@to_s
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Schema
|
|
6
|
+
#
|
|
7
|
+
# @see X222.pdf B.1.1.3.12.5 Data Segments in a Transaction Set
|
|
8
|
+
#
|
|
9
|
+
class SegmentUse < AbstractUse
|
|
10
|
+
include Inspect
|
|
11
|
+
|
|
12
|
+
# @see X222 B.1.1.3.12.7 Data Segment Position
|
|
13
|
+
#
|
|
14
|
+
# @return [Integer]
|
|
15
|
+
attr_reader :position
|
|
16
|
+
|
|
17
|
+
# @return [SegmentDef]
|
|
18
|
+
attr_reader :definition
|
|
19
|
+
|
|
20
|
+
def_delegators :definition, :id, :name, :descriptor, :code_lists
|
|
21
|
+
|
|
22
|
+
# @see X222 B.1.1.3.12.6 Data Segment Requirement Designators
|
|
23
|
+
#
|
|
24
|
+
# @return [SegmentReq]
|
|
25
|
+
attr_reader :requirement
|
|
26
|
+
|
|
27
|
+
def_delegators :requirement, :required?, :optional?
|
|
28
|
+
|
|
29
|
+
# @see X222 B.1.3.12.3 Repeated Occurrences of Single Data Segments
|
|
30
|
+
# @see X222 B.1.3.12.8 Data Segment Occurrence
|
|
31
|
+
#
|
|
32
|
+
# @return [RepeatCount]
|
|
33
|
+
attr_reader :repeat_count
|
|
34
|
+
|
|
35
|
+
# @return [LoopDef, TableDef]
|
|
36
|
+
attr_reader :parent
|
|
37
|
+
|
|
38
|
+
def initialize(definition, position, requirement, repeat_count, parent)
|
|
39
|
+
@definition, @position, @requirement, @repeat_count, @parent =
|
|
40
|
+
definition, position, requirement, repeat_count, parent
|
|
41
|
+
|
|
42
|
+
# Delay re-parenting until the entire definition tree has a root
|
|
43
|
+
# to prevent unnecessarily copying objects
|
|
44
|
+
unless parent.nil?
|
|
45
|
+
@definition = @definition.copy(:parent => self)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [SegmentUse]
|
|
50
|
+
def copy(changes = {})
|
|
51
|
+
SegmentUse.new \
|
|
52
|
+
changes.fetch(:definition, @definition),
|
|
53
|
+
changes.fetch(:position, @position),
|
|
54
|
+
changes.fetch(:requirement, @requirement),
|
|
55
|
+
changes.fetch(:repeat_count, @repeat_count),
|
|
56
|
+
changes.fetch(:parent, @parent)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def repeatable?
|
|
60
|
+
@repeat_count.try{|r| r.include?(2) }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [SegmentVal]
|
|
64
|
+
def empty(position)
|
|
65
|
+
definition.empty(self, position)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def value(element_vals, position)
|
|
69
|
+
definition.value(element_vals, self, position)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# (see AbstractUse#segment?)
|
|
73
|
+
def segment?
|
|
74
|
+
true
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @return [void]
|
|
78
|
+
def pretty_print(q)
|
|
79
|
+
q.text "SegmentUse"
|
|
80
|
+
q.group(2, "(", ")") do
|
|
81
|
+
q.breakable ""
|
|
82
|
+
q.pp @position
|
|
83
|
+
q.text ","
|
|
84
|
+
|
|
85
|
+
q.breakable
|
|
86
|
+
q.pp @definition
|
|
87
|
+
q.text ","
|
|
88
|
+
|
|
89
|
+
q.breakable
|
|
90
|
+
q.pp @requirement
|
|
91
|
+
q.text ","
|
|
92
|
+
|
|
93
|
+
q.breakable
|
|
94
|
+
q.pp @repeat_count
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Stupidedi
|
|
3
|
+
using Refinements
|
|
4
|
+
|
|
5
|
+
module Schema
|
|
6
|
+
#
|
|
7
|
+
# @see X222 B.1.1.3.1 Data Element
|
|
8
|
+
#
|
|
9
|
+
class SimpleElementDef < AbstractElementDef
|
|
10
|
+
# @return [SimpleElementUse, ComponentElementUse]
|
|
11
|
+
abstract :parent
|
|
12
|
+
|
|
13
|
+
# @return [SimpleElementVal]
|
|
14
|
+
abstract :empty, :args => %w(position)
|
|
15
|
+
|
|
16
|
+
# @return [SimpleElementVal]
|
|
17
|
+
abstract :value, :args => %w(object position)
|
|
18
|
+
|
|
19
|
+
# @return [SimpleElementVal]
|
|
20
|
+
abstract :parse, :args => %w(string position)
|
|
21
|
+
|
|
22
|
+
# @return true
|
|
23
|
+
def simple?
|
|
24
|
+
true
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return false
|
|
28
|
+
def composite?
|
|
29
|
+
false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [SimpleElementUse]
|
|
33
|
+
def simple_use(requirement, repeat_count, parent = nil)
|
|
34
|
+
SimpleElementUse.new(self, requirement, repeat_count, Sets.universal, parent)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [ComponentElementUse]
|
|
38
|
+
def component_use(requirement, parent = nil)
|
|
39
|
+
ComponentElementUse.new(self, requirement, Sets.universal, parent)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# This is overridden (if needed) by the concrete subclasses. Specifically
|
|
43
|
+
# the "ID" element types have an extra attribute to link the CodeList.
|
|
44
|
+
#
|
|
45
|
+
# @return [AbstractSet<CodeList>]
|
|
46
|
+
def code_lists(subset = Sets.universal)
|
|
47
|
+
Sets.empty
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|