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.
Files changed (176) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +319 -0
  3. data/LICENSE +26 -0
  4. data/README.md +308 -0
  5. data/Rakefile +33 -0
  6. data/bin/tediparse +102 -0
  7. data/doc/Defining.md +97 -0
  8. data/doc/Generating-Grammars.md +241 -0
  9. data/doc/Generating.md +332 -0
  10. data/doc/Navigating.md +670 -0
  11. data/doc/Parsing.md +133 -0
  12. data/doc/README.md +51 -0
  13. data/doc/Serializing.md +100 -0
  14. data/doc/Tokenizing.md +136 -0
  15. data/doc/Validating.md +140 -0
  16. data/lib/ruby/array.rb +226 -0
  17. data/lib/ruby/blank.rb +52 -0
  18. data/lib/ruby/exception.rb +14 -0
  19. data/lib/ruby/hash.rb +14 -0
  20. data/lib/ruby/module.rb +60 -0
  21. data/lib/ruby/object.rb +56 -0
  22. data/lib/ruby/string.rb +89 -0
  23. data/lib/ruby/to_d.rb +82 -0
  24. data/lib/ruby/to_date.rb +26 -0
  25. data/lib/ruby/to_time.rb +21 -0
  26. data/lib/ruby/try.rb +46 -0
  27. data/lib/stupidedi/builder.rb +6 -0
  28. data/lib/stupidedi/color.rb +94 -0
  29. data/lib/stupidedi/config/code_list_config.rb +45 -0
  30. data/lib/stupidedi/config/functional_group_config.rb +72 -0
  31. data/lib/stupidedi/config/interchange_config.rb +86 -0
  32. data/lib/stupidedi/config/transaction_set_config.rb +73 -0
  33. data/lib/stupidedi/config.rb +100 -0
  34. data/lib/stupidedi/either.rb +286 -0
  35. data/lib/stupidedi/exceptions/invalid_element_error.rb +7 -0
  36. data/lib/stupidedi/exceptions/invalid_schema_error.rb +7 -0
  37. data/lib/stupidedi/exceptions/missing_grammar_error.rb +50 -0
  38. data/lib/stupidedi/exceptions/output_error.rb +7 -0
  39. data/lib/stupidedi/exceptions/parse_error.rb +7 -0
  40. data/lib/stupidedi/exceptions/stupidedi_error.rb +7 -0
  41. data/lib/stupidedi/exceptions/tokenize_error.rb +7 -0
  42. data/lib/stupidedi/exceptions/zipper_error.rb +7 -0
  43. data/lib/stupidedi/exceptions.rb +13 -0
  44. data/lib/stupidedi/inspect.rb +26 -0
  45. data/lib/stupidedi/interchanges/element_types/separator_val.rb +79 -0
  46. data/lib/stupidedi/interchanges/element_types/special_val.rb +48 -0
  47. data/lib/stupidedi/interchanges/element_types.rb +11 -0
  48. data/lib/stupidedi/interchanges.rb +21 -0
  49. data/lib/stupidedi/parser/builder_dsl.rb +292 -0
  50. data/lib/stupidedi/parser/constraint_table.rb +566 -0
  51. data/lib/stupidedi/parser/generation.rb +147 -0
  52. data/lib/stupidedi/parser/identifier_stack.rb +248 -0
  53. data/lib/stupidedi/parser/instruction.rb +112 -0
  54. data/lib/stupidedi/parser/instruction_table.rb +222 -0
  55. data/lib/stupidedi/parser/navigation.rb +786 -0
  56. data/lib/stupidedi/parser/state_machine.rb +62 -0
  57. data/lib/stupidedi/parser/states/abstract_state.rb +385 -0
  58. data/lib/stupidedi/parser/states/failure_state.rb +69 -0
  59. data/lib/stupidedi/parser/states/functional_group_state.rb +101 -0
  60. data/lib/stupidedi/parser/states/initial_state.rb +62 -0
  61. data/lib/stupidedi/parser/states/interchange_state.rb +96 -0
  62. data/lib/stupidedi/parser/states/loop_state.rb +74 -0
  63. data/lib/stupidedi/parser/states/table_state.rb +90 -0
  64. data/lib/stupidedi/parser/states/transaction_set_state.rb +120 -0
  65. data/lib/stupidedi/parser/states/transmission_state.rb +59 -0
  66. data/lib/stupidedi/parser/tokenization.rb +195 -0
  67. data/lib/stupidedi/parser.rb +32 -0
  68. data/lib/stupidedi/reader/input/abstract_input.rb +137 -0
  69. data/lib/stupidedi/reader/input/delegated_input.rb +112 -0
  70. data/lib/stupidedi/reader/input/file_input.rb +157 -0
  71. data/lib/stupidedi/reader/input.rb +31 -0
  72. data/lib/stupidedi/reader/position.rb +78 -0
  73. data/lib/stupidedi/reader/result.rb +172 -0
  74. data/lib/stupidedi/reader/segment_dict.rb +176 -0
  75. data/lib/stupidedi/reader/separators.rb +90 -0
  76. data/lib/stupidedi/reader/stream_reader.rb +173 -0
  77. data/lib/stupidedi/reader/token_reader.rb +465 -0
  78. data/lib/stupidedi/reader/tokens/component_element_tok.rb +71 -0
  79. data/lib/stupidedi/reader/tokens/composite_element_tok.rb +85 -0
  80. data/lib/stupidedi/reader/tokens/repeated_element_tok.rb +74 -0
  81. data/lib/stupidedi/reader/tokens/segment_tok.rb +74 -0
  82. data/lib/stupidedi/reader/tokens/simple_element_tok.rb +76 -0
  83. data/lib/stupidedi/reader.rb +121 -0
  84. data/lib/stupidedi/schema/abstract_def.rb +76 -0
  85. data/lib/stupidedi/schema/abstract_element_def.rb +35 -0
  86. data/lib/stupidedi/schema/abstract_element_use.rb +47 -0
  87. data/lib/stupidedi/schema/abstract_use.rb +79 -0
  88. data/lib/stupidedi/schema/code_list.rb +99 -0
  89. data/lib/stupidedi/schema/component_element_use.rb +76 -0
  90. data/lib/stupidedi/schema/composite_element_def.rb +103 -0
  91. data/lib/stupidedi/schema/composite_element_use.rb +78 -0
  92. data/lib/stupidedi/schema/element_req.rb +57 -0
  93. data/lib/stupidedi/schema/functional_group_def.rb +124 -0
  94. data/lib/stupidedi/schema/generation/definition_generator.rb +139 -0
  95. data/lib/stupidedi/schema/generation/element_generator.rb +221 -0
  96. data/lib/stupidedi/schema/generation/flat_file_reader.rb +551 -0
  97. data/lib/stupidedi/schema/generation/functional_group_generator.rb +64 -0
  98. data/lib/stupidedi/schema/generation/interchange_generator.rb +145 -0
  99. data/lib/stupidedi/schema/generation/master_loader_generator.rb +121 -0
  100. data/lib/stupidedi/schema/generation/models.rb +85 -0
  101. data/lib/stupidedi/schema/generation/module_loader_generator.rb +64 -0
  102. data/lib/stupidedi/schema/generation/registration_generator.rb +230 -0
  103. data/lib/stupidedi/schema/generation/runner.rb +161 -0
  104. data/lib/stupidedi/schema/generation/segment_generator.rb +130 -0
  105. data/lib/stupidedi/schema/generation/support.rb +78 -0
  106. data/lib/stupidedi/schema/generation/support_modules_generator.rb +126 -0
  107. data/lib/stupidedi/schema/generation/version_modules.rb +35 -0
  108. data/lib/stupidedi/schema/generation.rb +94 -0
  109. data/lib/stupidedi/schema/interchange_def.rb +103 -0
  110. data/lib/stupidedi/schema/loop_def.rb +156 -0
  111. data/lib/stupidedi/schema/repeat_count.rb +86 -0
  112. data/lib/stupidedi/schema/segment_def.rb +122 -0
  113. data/lib/stupidedi/schema/segment_req.rb +46 -0
  114. data/lib/stupidedi/schema/segment_use.rb +99 -0
  115. data/lib/stupidedi/schema/simple_element_def.rb +51 -0
  116. data/lib/stupidedi/schema/simple_element_use.rb +83 -0
  117. data/lib/stupidedi/schema/syntax_note.rb +52 -0
  118. data/lib/stupidedi/schema/table_def.rb +178 -0
  119. data/lib/stupidedi/schema/transaction_set_def.rb +125 -0
  120. data/lib/stupidedi/schema.rb +30 -0
  121. data/lib/stupidedi/sets.rb +42 -0
  122. data/lib/stupidedi/transaction_sets/builder/dsl.rb +192 -0
  123. data/lib/stupidedi/transaction_sets/builder.rb +188 -0
  124. data/lib/stupidedi/transaction_sets/common/implementations/element_reqs.rb +37 -0
  125. data/lib/stupidedi/transaction_sets/common/implementations/segment_reqs.rb +31 -0
  126. data/lib/stupidedi/transaction_sets/common/implementations.rb +11 -0
  127. data/lib/stupidedi/transaction_sets/common.rb +8 -0
  128. data/lib/stupidedi/transaction_sets/validation/ambiguity.rb +395 -0
  129. data/lib/stupidedi/transaction_sets/validation/implementation.rb +12 -0
  130. data/lib/stupidedi/transaction_sets/validation.rb +9 -0
  131. data/lib/stupidedi/transaction_sets.rb +25 -0
  132. data/lib/stupidedi/values/abstract_element_val.rb +19 -0
  133. data/lib/stupidedi/values/abstract_val.rb +133 -0
  134. data/lib/stupidedi/values/composite_element_val.rb +102 -0
  135. data/lib/stupidedi/values/functional_group_val.rb +105 -0
  136. data/lib/stupidedi/values/interchange_val.rb +102 -0
  137. data/lib/stupidedi/values/invalid_envelope_val.rb +61 -0
  138. data/lib/stupidedi/values/invalid_segment_val.rb +89 -0
  139. data/lib/stupidedi/values/loop_val.rb +73 -0
  140. data/lib/stupidedi/values/repeated_element_val.rb +113 -0
  141. data/lib/stupidedi/values/segment_val.rb +105 -0
  142. data/lib/stupidedi/values/segment_val_group.rb +20 -0
  143. data/lib/stupidedi/values/simple_element_val.rb +80 -0
  144. data/lib/stupidedi/values/table_val.rb +69 -0
  145. data/lib/stupidedi/values/transaction_set_val.rb +69 -0
  146. data/lib/stupidedi/values/transmission_val.rb +56 -0
  147. data/lib/stupidedi/values.rb +22 -0
  148. data/lib/stupidedi/version.rb +4 -0
  149. data/lib/stupidedi/versions/common/element_reqs.rb +13 -0
  150. data/lib/stupidedi/versions/common/element_types/an.rb +386 -0
  151. data/lib/stupidedi/versions/common/element_types/dt.rb +572 -0
  152. data/lib/stupidedi/versions/common/element_types/id.rb +304 -0
  153. data/lib/stupidedi/versions/common/element_types/nn.rb +312 -0
  154. data/lib/stupidedi/versions/common/element_types/operators.rb +128 -0
  155. data/lib/stupidedi/versions/common/element_types/r.rb +342 -0
  156. data/lib/stupidedi/versions/common/element_types/simple_element.rb +73 -0
  157. data/lib/stupidedi/versions/common/element_types/tm.rb +347 -0
  158. data/lib/stupidedi/versions/common/element_types.rb +29 -0
  159. data/lib/stupidedi/versions/common/segment_reqs.rb +15 -0
  160. data/lib/stupidedi/versions/common/syntax_notes.rb +172 -0
  161. data/lib/stupidedi/versions/common.rb +11 -0
  162. data/lib/stupidedi/versions.rb +25 -0
  163. data/lib/stupidedi/writer/claredi.rb +178 -0
  164. data/lib/stupidedi/writer/default.rb +119 -0
  165. data/lib/stupidedi/writer.rb +7 -0
  166. data/lib/stupidedi/zipper/abstract_cursor.rb +351 -0
  167. data/lib/stupidedi/zipper/dangling_cursor.rb +103 -0
  168. data/lib/stupidedi/zipper/edited_cursor.rb +157 -0
  169. data/lib/stupidedi/zipper/memoized_cursor.rb +133 -0
  170. data/lib/stupidedi/zipper/path.rb +132 -0
  171. data/lib/stupidedi/zipper/root_cursor.rb +120 -0
  172. data/lib/stupidedi/zipper/stack_cursor.rb +107 -0
  173. data/lib/stupidedi/zipper.rb +45 -0
  174. data/lib/stupidedi.rb +69 -0
  175. data/lib/tediparse.rb +1 -0
  176. 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