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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8a9edcb495d00d92eb5add48fa6d6706db419e3a78c0f01b96519fb22034c501
4
+ data.tar.gz: a862a70cdd1bbfb4aeb925cc9203a8794d7c4a3113c1d92527920629d4d710d9
5
+ SHA512:
6
+ metadata.gz: fc2fd8ef0383d83016259c68757c5c1a7434c4c77f4357566f43c432676e08e5ba9884ee7dc010d415c2d253f140349612a7b8b0d715642ef6be4b6609f9874f
7
+ data.tar.gz: 305d907ad0b62760ccc75556df795512d46cc4f85d3c897a6fe9a06d4b89a58f76ba6121545af1b2f1676b555c248046a5c597c1ba0fb6ccbbc7f52b9e3a3cdf
data/CHANGELOG.md ADDED
@@ -0,0 +1,319 @@
1
+ v 2.0.0 - Aug 27, 2026
2
+
3
+ First release of tediparse. The version continues the `stupidedi`
4
+ numbering (upstream's last release was 1.4.5) and steps the major to
5
+ mark the breaking removal of X12 grammar content described below.
6
+
7
+ Tediparse is now a maintained fork of `stupidedi` (Kyle Putnam, upstream
8
+ maintained by Isi Robayna). The internal `Stupidedi` module name is
9
+ preserved for source compatibility — `require "tediparse"` and `require
10
+ "stupidedi"` both work.
11
+
12
+ **Breaking: X12 grammar content removed**
13
+
14
+ Tediparse ships the engine, not the grammars. Roughly 856 files of X12
15
+ IP have been removed from `lib/`: every per-era version namespace
16
+ (`Versions::TwoThousandOne` through `FiftyTen`), every transaction-set
17
+ definition (standards and HIPAA implementations under
18
+ `transaction_sets/<version>/`), every interchange envelope
19
+ (`interchanges/{00200,00300,00400,00401,00501}/`), and the
20
+ `editor/` / `contrib/` / `guides/` trees.
21
+
22
+ Before relying on tediparse you must register your own grammar against
23
+ `Stupidedi::Config`. See `spec/support/synthetic/` in the source tree
24
+ for a worked authoring example.
25
+
26
+ * **Recovering the upstream corpus.** The full content tree and the
27
+ ~146 `.edi` fixtures live at the `pre-x12-removal` git tag. Use
28
+ `git worktree add ../conformance pre-x12-removal` to drive a private
29
+ conformance suite against the engine while keeping X12 IP out of
30
+ the published gem.
31
+ * `Validation::Ambiguity.build` now requires an `InterchangeDef` as
32
+ a third positional argument — pass your synthetic envelope (see
33
+ `spec/support/synthetic/interchange_def.rb`). The previous
34
+ nil-default would crash inside `mkconfig` when the validator
35
+ actually ran.
36
+
37
+ **Helpful failure surface**
38
+
39
+ * Add `Stupidedi::Exceptions::MissingGrammarError` (subclass of
40
+ `StupidediError`). Const lookups against the removed namespaces —
41
+ `Stupidedi::Editor`, `Stupidedi::Contrib`, `Stupidedi::Guides`,
42
+ the per-era `Stupidedi::Versions::FiftyTen` /
43
+ `Stupidedi::TransactionSets::FortyTen` /
44
+ `Stupidedi::Interchanges::FiveOhOne` style references, plus the
45
+ long-deprecated `Versions::FunctionalGroups` /
46
+ `Versions::Interchanges` aliases — raise it with a message that
47
+ names the requested constant and points users at the authoring
48
+ reference. Legitimate typos still surface as `NameError`.
49
+ * Parser-driven lookups against an empty config now push a
50
+ `FailureState` whose reason is the same helpful message instead of
51
+ the generic "unknown … version" string. Applies to all three
52
+ parser states (`InterchangeState`, `FunctionalGroupState`,
53
+ `TransactionSetState`).
54
+ * `Config::InterchangeConfig`, `FunctionalGroupConfig`, and
55
+ `TransactionSetConfig` gain an `empty?` predicate.
56
+ * `InterchangeConfig#register`, `FunctionalGroupConfig#register`, and
57
+ `TransactionSetConfig#register` now raise `ArgumentError` when
58
+ called with neither a definition nor a block, instead of silently
59
+ storing `nil` under the key. Catches the `register("DEMO01")` typo
60
+ that would otherwise produce a confusing `NoMethodError` deep in
61
+ the parser.
62
+
63
+ **Removed**
64
+
65
+ * `bin/edi-pp`, `bin/edi-ed`, `bin/edi-obfuscate` — the gem no longer
66
+ ships executables.
67
+ * `lib/stupidedi/editor/` and `editor.rb` (the entire TA1 / 999 /
68
+ 277CA acknowledgement subsystem).
69
+ * `lib/stupidedi/contrib/`, `contrib.rb`, `guides.rb`.
70
+ * Per-era autoloads from `lib/stupidedi.rb`, `interchanges.rb`,
71
+ `versions.rb`, `transaction_sets.rb`.
72
+ * `Config.default` / `Config.hipaa` / `Config.contrib` factory bodies
73
+ — the methods are preserved as documented no-ops returning a fresh
74
+ empty `Config`, for source compatibility.
75
+ * `Stupidedi::Config::EditorConfig` and the `Config#editor` reader —
76
+ the editor registry only had callers inside the now-deleted
77
+ `lib/stupidedi/editor/` tree.
78
+ * Top-level `notes/` scratch directory — historical maintainer
79
+ examples (X12 generator, recovery script, JSON writer demo) that
80
+ all referenced the deleted X12 grammar tree. Still recoverable
81
+ from the `pre-x12-removal` tag.
82
+ * Top-level `examples/` directory (`generate.rb`, `tokenizer.rb`) —
83
+ both scripts were hard-wired to `Config.hipaa` and crashed at
84
+ runtime against the now-empty config. `spec/support/synthetic/` is
85
+ the canonical worked example; the old scripts are still recoverable
86
+ from the `pre-x12-removal` tag.
87
+
88
+ **Added**
89
+
90
+ * `spec/support/synthetic/` — a small non-X12 grammar harness
91
+ (interchange + functional group + transaction set + adversarial
92
+ variants) used to exercise the engine in tests. Doubles as the
93
+ canonical grammar-authoring reference.
94
+
95
+ * Grammar generation supports release `003060` (module `ThirtySixty`,
96
+ interchange version `00306`). Its ASC X12 Table Data is pure ASCII,
97
+ and is declared as Windows-1252 in
98
+ `FlatFileReader::SOURCE_ENCODINGS` alongside the other pre-008010
99
+ releases rather than being left to the UTF-8 default.
100
+
101
+ **Bug fixes**
102
+
103
+ * `Schema::Generation::FlatFileReader` now rejects two shapes of
104
+ defective SETDETL row instead of carrying them into the generated
105
+ grammar. Both are column shifts in the source distribution — a value
106
+ landing one field left of where it belongs — which the neighbouring
107
+ release carries correctly:
108
+
109
+ - A blank or zero loop repeat, or a blank maximum use, produced
110
+ `RepeatCount.bounded(0)`. That is not a repeat count the engine can
111
+ build, so the emitted grammar raised when the consumer loaded it,
112
+ naming neither the release nor the transaction set nor the row.
113
+ - `"0"` in the loop-id column (whatever the repeat says) opened a
114
+ loop identified as `"0"`. Nothing objects to that: it builds, loads
115
+ and parses, quietly reparenting every row that follows it — in one
116
+ real case the rest of the heading table, which then could not be
117
+ parsed past that point.
118
+
119
+ The reader now raises at generation time, naming the file,
120
+ transaction set, area, sequence and segment, so the row can be
121
+ corrected in the table data.
122
+
123
+ * `Schema::RepeatCount.bounded` raised
124
+ `Exception::InvalidSchemaError`, which resolves to `::Exception` and
125
+ has no such constant, so a non-positive count surfaced as
126
+ `NameError: uninitialized constant Exception::InvalidSchemaError`
127
+ rather than the intended `InvalidSchemaError, "n must be positive"`.
128
+
129
+ * `Schema::Generation::FlatFileReader` declared the wrong encoding for
130
+ every supported release. It read the ASC X12 Table Data as ISO-8859-1;
131
+ 004060 through 007010 are Windows-1252 (so CP1252 smart punctuation at
132
+ 0x80-0x9F decoded to C1 control characters) and 008010 is UTF-8 (so
133
+ every multi-byte character was double-encoded). Both transcoded
134
+ silently, and the damage reached the generated grammar. The encoding is
135
+ now declared per release in `FlatFileReader::SOURCE_ENCODINGS`; an
136
+ undeclared release is read as UTF-8, deliberately, so a wrong assumption
137
+ raises instead of writing mojibake. Decoded text has CP1252 smart
138
+ quotes and dashes normalized to ASCII (accented letters are left
139
+ alone), and a C1 control character surviving the decode raises with the
140
+ file, line and codepoint. **Consumers should regenerate their grammar
141
+ tree**: affected element and segment names change.
142
+
143
+ **Bug fixes** (carried over from prior fork work)
144
+
145
+ * Fix `too much non-determinism` error when a `LoopDef` contains repeated
146
+ structurally-identical `SegmentUse` slots (same id, no qualifier element)
147
+ and only the earlier slot is filled — e.g. an N3 in the partner-customised
148
+ 4010 PO850 N1 loop. `ConstraintTable::ValueBased` now consults the active
149
+ parser state's parse tree to pick the slot whose preceding sibling has
150
+ actually been consumed: the earliest position past the highest-consumed
151
+ sibling in the matching parent loop. This requires plumbing the active
152
+ `AbstractState` through `InstructionTable#matches` and the `ConstraintTable`
153
+ subclasses' `matches`. Behaviour is unchanged for qualifier-disambiguated
154
+ paths (e.g. N1 by N101) and for genuinely ambiguous cases across different
155
+ parent loops. During `find` navigation the `mode == :insert` gate skips
156
+ disambiguation entirely, so the full candidate set is preserved.
157
+ * Build out 5010 SO317 as a test case for interleaved segments and child
158
+ loops within a single `LoopDef`.
159
+
160
+ v 1.4.1
161
+
162
+ **Bug Fixes**
163
+
164
+ * Fix missing method delegations in SimpleElementUse, ComponentElementUse, and CompositeElementUse #185
165
+ * Fix regression in edi-obfuscate
166
+ * Fix copy-pasted StringVal -> IdentifierVal #187
167
+ * Fix crash in 4010 editor, wrong method called #188
168
+ * Fix bug in RepeatedElementVal#==, incorrect comparison #190
169
+ * Fix error message when required composite element is missing #194
170
+ * Fix various typos in comments and descriptor strings #230 and #233
171
+
172
+ **Added**
173
+
174
+ * Add DSL for defining X12 grammars TransactionSets::Builder::Dsl #200
175
+ * Add support for 5010 X12-HN277 Health Care Information Status Notification
176
+ * Add support for 5010 X220A1-BE834 BGN05 Time Code #205
177
+ * Add support for "02 - Birth" maintenance reason code #209
178
+
179
+ v 1.4.0
180
+
181
+ **Bug fixes**
182
+
183
+ * Fix ambiguous grammars (mostly due to incorrect parentheses)
184
+ * Fix errors in `Standards::FortyTen::HC837`
185
+ * Fix errors in `Standards::FiftyTen::BE834`
186
+ * Fix errors in `Standards::FiftyTen::HB271`
187
+ * Fix errors in `Standards::FiftyTen::RA820`
188
+ * Fix parsing invalid numeric data in Ruby 2.4+. Previously `"10B"` would be read as `10.0`, and `"AB10"` would be read as `0.0` due to using bigdecimal/util's implementation of `String#to_d`
189
+ * Fix `TimeVal` issue in all versions (not only `005010`)
190
+
191
+ **Added**
192
+
193
+ * Add `Stupidedi::Parser.build` as a shortcut for `Stupidedi::Parser::StateMachine.build`
194
+ * Add many stub definitions of segments, just the segment name and no elements, which are referred to by `RA820` and others.
195
+ * `edi-pp` can now print different formats with `--format html`, `--format x12`, and `--format tree` (default)
196
+
197
+ **Deprecation notices**
198
+
199
+ * Remove support for Ruby < 2.0
200
+ * Remove workarounds for broken JRuby refinements
201
+ * Remove `Symbol#call` and `Symbol#to_proc` refinements
202
+
203
+ **Renamed**
204
+
205
+ * `Stupidedi::Builder` is renamed to `Stupidedi::Parser`
206
+ * `Stupidedi::Guides::*::GuideBuilder` is renamed to `Stupidedi::TransactionSets::Builder`
207
+ * `Stupidedi::Versions::Interchanges` is renamed to `Stupidedi::Interchanges`
208
+ * `Stupidedi::Versions::FunctionalGroups` is renamed to `Stupidedi::Versions`
209
+ * Lots of common code among versions has been factored into `Stupidedi::Versions::Common`
210
+ * Rename Guides `HC837P` and `HC837I` to `HC837`
211
+ * Moved all grammars, including `Guides` and `Contrib`, to `Stupidedi::TransactionSets`
212
+ * Each version now has `::Standards` and `::Implementations`
213
+ * `Stupidedi::Schema::Auditor` is renamed to `Stupidedi::TransactionSets::Validation::Ambiguity`
214
+
215
+ Most of these renames are not breaking changes (yet), but using the old name will print a warning:
216
+
217
+ ```
218
+ Stupidedi::Contrib is deprecated, use Stupidedi::TransactionSets
219
+ Stupidedi::Guides is deprecated, use Stupidedi::TransactionSets::*::Implementations
220
+ Stupidedi::TransactionSets::FiftyTen::Implementations::X222::HC837P is deprecated, use HC837 instead
221
+ Stupidedi::TransactionSets::FiftyTen::Implementations::X222A1::HC837P is deprecated, use HC837 instead
222
+ Stupidedi::TransactionSets::FiftyTen::Implementations::X223::HC837I is deprecated, use HC837 instead
223
+ Stupidedi::Versions::Interchanges is deprecated, use Stupidedi::Interchanges instead
224
+ Stupidedi::Versions::FunctionalGroups is deprecated, use Stupidedi::TransactionSets::*::Standards instead
225
+ ```
226
+
227
+ **Specs**
228
+
229
+ * Grammar specs automatically created when a fixture is added to spec/fixtures/<version>/<name>/pass/*.x12
230
+ * Remove support for `rcov`. Use only `simplecov` now
231
+ * Update all specs to use `expect(value).to matcher` syntax, instead of `value.should matcher`
232
+ * New specs to ensure element names match their `id` (eg, `E123.id == :E123`)
233
+ * New specs to ensure segment names match their `id` (eg `ST.id == :ST`)
234
+ * New specs to ensure `Config.hipaa`, `Config.contrib`, and `Config.default` reference valid definitions
235
+ * New specs for `Stupidedi::TransactionSets::Validation::Ambiguity`
236
+ * Fix fixture files that used `\n` as a segment terminator but didn't have one after `IEA`
237
+
238
+ **Miscellaneous**
239
+
240
+ * Create new examples in `examples/` that demonstrate undocumented `IdentifierStack`, and more
241
+ * Made whitespace and other formatting more consistent
242
+ * `Stupidedi::TransactionSets::Builder.build` no longer requires a `TransactionSetDef` argument
243
+ * Fix Travis CI to build older versions of Ruby < 2.3
244
+ * Ignore large definition files in Code Climate
245
+
246
+ v 1.3.24
247
+ - Fix repeatability test in Navigation#iterate
248
+ - Adds implementation of June 2014 005010X223A3 (837I)
249
+ - Fixes misplaced 2330H and 2330I loops. Fixes names for 2310 Occurrenc…
250
+
251
+ v 1.3.23 - Jan 10, 2019
252
+ - Fix decimal values for TimeVal being coerced incorrectly https://github.com/irobayna/stupidedi/pull/151
253
+ - Detect ambiguous grammar automatically https://github.com/irobayna/stupidedi/pull/153
254
+
255
+ v 1.3.22
256
+ - Re-enable EC Segment on FifyTen group
257
+
258
+ v 1.3.21 - Dec 9, 2018 ** Breaking Changes **
259
+ - Throw exception from #iterate if segment is not repeatable (fix #126) https://github.com/irobayna/stupidedi/pull/146
260
+ - Configurable limit of non-determinism (fixes #129) https://github.com/irobayna/stupidedi/pull/145
261
+ - Add utility to obfuscate data in X12 files https://github.com/irobayna/stupidedi/pull/147
262
+ - Fix potential frozen string issues in pretty_print methods https://github.com/irobayna/stupidedi/pull/148
263
+ - Remove erroneous code mappings from element definitions
264
+
265
+ v 1.2.20 - Oct 23, 2018
266
+ - Json Writer functionality - Traverse stupidedit internal tree to a ruby hash
267
+ - Ruby 2.5.3 support
268
+
269
+ v 1.2.19 - Oct 8, 2018
270
+ - EDI 276 support - Health Care Claim Status Inquiry
271
+ X212-HR276
272
+
273
+ v 1.2.18 -
274
+ - SH856 rework
275
+ - PR855 support (v4010)
276
+ - This change fixes this issue, as if decimal is an empty string, it will be changed to 0 before to_d is called on it
277
+ https://github.com/irobayna/stupidedi/pull/132
278
+ Update lib/stupidedi/versions/functional_groups/004010/element_types/time_val.rb
279
+
280
+
281
+ v 1.2.17 - Aug 4, 2018
282
+ - EDI 270 / 271 support - Health Care Eligibility Benefit Inquiry and Response
283
+ X279-HS270
284
+ X279-HB271
285
+ X279A1-HS270
286
+ X279A1-HB271
287
+
288
+ v 1.2.16 - May 27, 2018
289
+ - Fix item #127 (https://github.com/irobayna/stupidedi/issues/127)
290
+ - Ruby 2.5.1 support
291
+
292
+ v 1.2.15 - Dec 20, 2017
293
+ - Gemfile Updates (fix security vulnerability CVE-2017-17042)
294
+ - Add Ruby 2.5.0-preview1 support
295
+ - Add 2.4.3 & 2.5.0-preview1 ruby versions to Travis CI
296
+
297
+ v 1.2.14 - June 19, 2017
298
+ - Gemfile Updates
299
+ - use BigDecimal string refinement only on ruby versions < 2.4
300
+ - remove rake from gemspec
301
+ - Add Ruby 2.4.x support
302
+
303
+ v 1.2.12 - July 29, 2016
304
+ - Fix a few issues with immutable strings
305
+
306
+ v 1.2.2 - July 8, 2014
307
+ - Remove definition of module Enumerable::blank? and present?
308
+ - Add blank?, present? to Array class
309
+
310
+ v 1.2.1 - July 7, 2014
311
+ - Don't redefine 'blank?' for Enumerable, if an implementation already (rails support)
312
+
313
+ v 1.2.0 - July 7, 2014
314
+ - Don't redefine try, if active_support provides an implementation already (better rails support)
315
+
316
+ v 1.1.0 - June 24, 2014
317
+ - Rspec 3.x support
318
+ - Drop Ruby 1.8.7 support
319
+ - Add Ruby 2.1.2 support
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2013, Kyle Putnam
2
+ Copyright (c) 2025, Adrian Duyzer / Tediware
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+ * Neither the name of stupidedi nor the names of its contributors
14
+ may be used to endorse or promote products derived from this software
15
+ without specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,308 @@
1
+ # Tediparse
2
+
3
+ - [GitHub project](https://github.com/Tediware/tediparse)
4
+ - [Human Documentation](doc/README.md)
5
+
6
+ Tediparse is a library for **parsing, generating, and validating** ASC X12 EDI
7
+ documents. You bring the X12 transaction-set grammar; the library walks it.
8
+ Very roughly, it's jQuery for EDI — once you've supplied the schema.
9
+
10
+ For those unfamiliar with ASC X12 EDI, it is a data format used to encode
11
+ common business documents like purchase orders, delivery notices, and health
12
+ care claims. It is similar to XML in some ways, but precedes it by about 15
13
+ years; so if you think XML sucks, you will love to hate EDI.
14
+
15
+ ## Scope and licensing
16
+
17
+ **Tediparse ships the engine, not the grammars.** The library does not bundle
18
+ any X12 transaction-set definitions, code lists, envelopes, or ack/editor
19
+ content — that material is X12 IP and is not ours to redistribute. To parse
20
+ or generate real X12 documents you must supply (or license) your own grammar
21
+ and register it against `Stupidedi::Config` before calling the parser.
22
+
23
+ If you reach for one of the per-era namespaces that the upstream `stupidedi`
24
+ gem shipped — `Stupidedi::Versions::FiftyTen`, `Stupidedi::TransactionSets::FortyTen`,
25
+ `Stupidedi::Interchanges::FiveOhOne`, and so on — tediparse raises a typed
26
+ `Stupidedi::Exceptions::MissingGrammarError` with guidance, rather than
27
+ `NameError`. Parser-driven lookups against an empty config produce a
28
+ `FailureState` whose reason carries the same message.
29
+
30
+ **Engine scope.** Tediparse provides parsing and generation as a library. The
31
+ upstream gem's editor / acknowledgement subsystem (TA1 / 999 / 277CA) and
32
+ the `bin/edi-pp` / `edi-ed` / `edi-obfuscate` command-line tools are not
33
+ part of tediparse.
34
+
35
+ ### Authoring a grammar
36
+
37
+ The canonical reference for how to wire up your own grammar against the
38
+ engine lives in the spec harness:
39
+
40
+ - `spec/support/synthetic/demo.rb` — a small adversarial transaction set
41
+ built with `Stupidedi::TransactionSets::Builder`. Exercises composites,
42
+ code lists, interleaved segments and child loops, qualifier-discriminated
43
+ sibling loops, and the `P` syntax note.
44
+ - `spec/support/synthetic/interchange_def.rb` — minimal ISA envelope.
45
+ - `spec/support/synthetic/functional_group_def.rb` — minimal GS envelope.
46
+ - `spec/support/synthetic/config.rb` — wires the three pieces into a usable
47
+ `Stupidedi::Config`.
48
+
49
+ Mirror that shape in your own application and register against
50
+ `Stupidedi::Config.new` (the legacy `Config.default` / `hipaa` / `contrib`
51
+ factories are preserved for source compatibility but now return empty
52
+ configs).
53
+
54
+ ### Generating a grammar from ASC X12 Table Data
55
+
56
+ Hand-authoring is fine for a handful of segments, but a full transaction set
57
+ is hundreds of definitions. With an **ASC X12 license** you can download the
58
+ **Table Data** (the official `.TXT` distribution — `ELEHEAD`, `SEGDETL`,
59
+ `SETDETL`, `FREEFORM`, etc.) from <https://ecommerce.x12.org/downloads>, and
60
+ tediparse can generate the grammar definition files for you. The full
61
+ walkthrough — input file formats, the output tree, loading, multi-release
62
+ trees, and how it works — is in
63
+ [doc/Generating-Grammars.md](doc/Generating-Grammars.md); the short version:
64
+
65
+ ```sh
66
+ tediparse generate --release 005010 \
67
+ --table-data vendor/x12/table_data/005010 \
68
+ --out lib
69
+ ```
70
+
71
+ or from Ruby:
72
+
73
+ ```ruby
74
+ Stupidedi::Schema::Generation.run(
75
+ table_data: "vendor/x12/table_data/005010",
76
+ release: "005010",
77
+ out: "lib",
78
+ namespace: "Edi" # root module for the emitted code (default: "Edi")
79
+ )
80
+ ```
81
+
82
+ This reads the flat files and writes a grammar tree under `<out>/<namespace>/`
83
+ — per-version support modules, `element_defs.rb`, `segment_defs.rb`,
84
+ `functional_group_def.rb`, one file per transaction set under `standards/`, an
85
+ interchange envelope under `interchanges/`, and a `stupidedi_registration.rb`
86
+ that wires everything onto a `Config`. Use `--dry-run` to preview without
87
+ writing. Supported releases: `003060`, `004010`, `004060`, `005010`, `006010`,
88
+ `007010`, `008010`.
89
+
90
+ You can keep several releases in one output tree. Generating an additional
91
+ release preserves the ones already present: `stupidedi_registration.rb` (and the
92
+ master loader, if any) are whole-tree artifacts that are rebuilt to cover every
93
+ release in `out`, not just the one you just generated. To rebuild those
94
+ aggregation files on their own — e.g. after adding or removing a release by other
95
+ means — run `tediparse register --out DIR` (or `Stupidedi::Schema::Generation.register(out:)`).
96
+
97
+ By default the generated tree is loaded by your application's autoloader (e.g.
98
+ Rails/Zeitwerk). If you are not using an autoloader, pass `--master-loader`
99
+ (or `master_loader: true`) to also emit a single entry file (`<namespace>.rb`,
100
+ e.g. `edi.rb`) that `require`s the whole tree in dependency order, so
101
+ `require "edi"` is all you need.
102
+
103
+ The X12 Table Data you supply is licensed X12 IP, and the generated grammar is
104
+ a derivative you own — **neither ships with this gem.** The generator is the
105
+ machine; you bring the material. The committed fixture under
106
+ `spec/support/generation/table_data/` is a hand-written synthetic grammar in
107
+ the same flat-file format, not real X12 content.
108
+
109
+ ### Conformance suite
110
+
111
+ If you have private grammars and need to keep them tested against tediparse,
112
+ the upstream X12 fixture corpus and grammar tree remain recoverable from the
113
+ pre-removal commit:
114
+
115
+ git fetch --tags
116
+ git worktree add ../conformance pre-x12-removal
117
+
118
+ Build the conformance suite against that worktree's fixtures, layer your
119
+ licensed grammar on top, and run it out-of-tree.
120
+
121
+ ## Attribution
122
+
123
+ This product includes software from `stupidedi` by Kyle Putnam, available at
124
+ <https://github.com/kputnam/stupidedi>.
125
+
126
+ ### Fork relationship
127
+
128
+ Tediparse is a fork of [stupidedi](https://github.com/kputnam/stupidedi),
129
+ maintained by [Adrian Duyzer](https://github.com/adriand) at
130
+ [Tediware](https://github.com/Tediware). The fork was created to accelerate
131
+ development and support a broader set of X12 documents. The internal Ruby
132
+ module name remains `Stupidedi` for backward compatibility; `require "tediparse"` and `require "stupidedi"` both work.
133
+
134
+ ### Credits
135
+
136
+ - **Original author**: [Kyle Putnam](https://github.com/kputnam)
137
+ - **Tediparse maintainer**: [Adrian Duyzer](https://github.com/adriand)
138
+
139
+ ## What problem does it solve?
140
+
141
+ Transaction set specifications can be enormous, boring, and vague. Trading
142
+ partners can demand strict adherence (often to their own unique
143
+ interpretation of the specification) of the documents you generate. However,
144
+ documents they generate themselves are often non-standard and require
145
+ flexibility to parse them.
146
+
147
+ Tediparse enables you to encode these transaction set specifications directly
148
+ in Ruby. From these specifications, it will generate a parser to read
149
+ incoming messages and a DSL to generate outgoing messages. This approach has
150
+ a huge advantage over writing a parser from scratch, which can be error-prone
151
+ and difficult to change.
152
+
153
+ ### Robust tokenization and parsing
154
+
155
+ Delimiters, line breaks, and out-of-band data between interchanges are
156
+ handled correctly. While many trading partners follow common conventions, it
157
+ only takes one unexpected deviation, like swapping the ":" and "~"
158
+ delimiters, to render a hand-written parser broken.
159
+
160
+ Tediparse handles many edge cases that can only be anticipated by reading
161
+ carefully between the lines of the X12 documentation.
162
+
163
+ ### Instant feedback on error conditions
164
+
165
+ When generating EDI documents, validation is performed incrementally on each
166
+ segment. This means the instant your client code violates the specification,
167
+ an exception is thrown with a meaningful stack trace. Other libraries only
168
+ perform validation after the entire document has been generated, while some
169
+ don't perform validation at all.
170
+
171
+ ### Encourages readable client code
172
+
173
+ Unlike other libraries, generating documents doesn't involve naming obscure
174
+ identifiers from the specification (like C001, DE522 or LOOP2000), for
175
+ elements of the grammar that don't actually appear in the output.
176
+
177
+ Like HAML or Builder::XmlMarkup, the DSL for generating documents closely
178
+ matches terminology from the problem domain. You can see in the example
179
+ below that code looks very similar to an EDI document.
180
+
181
+ ### Efficient parsing and traversing
182
+
183
+ The parser is designed using immutable data structures, making it thread-safe
184
+ for runtimes that can utilize multiple cores. In some cases, immutability
185
+ places higher demand on garbage collection; this has been somewhat mitigated
186
+ with careful optimization.
187
+
188
+ ## Examples
189
+
190
+ The examples below assume `config` is a `Stupidedi::Config` you've populated
191
+ with your own grammar — see `spec/support/synthetic/config.rb` for the
192
+ authoring pattern.
193
+
194
+ ### Generating, Writing
195
+
196
+ #### X12 Writer
197
+
198
+ ```ruby
199
+ require "tediparse"
200
+
201
+ # You bring the grammar. See spec/support/synthetic for the authoring shape.
202
+ config = MyApp::EDI.config
203
+
204
+ b = Stupidedi::Parser::BuilderDsl.build(config)
205
+
206
+ # These methods perform error checking: number of elements, element types, min/max
207
+ # length requirements, conditionally required elements, valid segments, number of
208
+ # segment occurrences, number of loop occurrences, etc.
209
+ b.ISA "00", nil, "00", nil,
210
+ "ZZ", "SUBMITTER ID",
211
+ "ZZ", "RECEIVER ID",
212
+ "990531", "1230", nil, "00501", "123456789", "1", "T", nil
213
+
214
+ # The API tracks the current position in the specification (e.g., the current loop,
215
+ # table, etc) to ensure well-formedness as each segment is generated.
216
+ b.GS "HC", "SENDER ID", "RECEIVER ID", "19990531", "1230", "1", "X", "005010X222"
217
+
218
+ b.ST "837", "1234", b.default
219
+ b.BHT "0019", "00", "X"*30, "19990531", Time.now.utc, "CH"
220
+ b.NM1 b.default, "1", "PREMIER BILLING SERVICE", nil, nil, nil, nil, "46", "12EEER000TY"
221
+ # ...
222
+
223
+ b.machine.zipper.tap do |z|
224
+ separators =
225
+ Stupidedi::Reader::Separators.build :segment => "~\n",
226
+ :element => "*",
227
+ :component => ":",
228
+ :repetition => "^"
229
+
230
+ w = Stupidedi::Writer::Default.new(z.root, separators)
231
+ print w.write()
232
+ end
233
+ ```
234
+
235
+ #### HTML writer
236
+
237
+ `Stupidedi::Writer::Default` outputs plain X12; `Stupidedi::Writer::Claredi`
238
+ outputs a formatted HTML string.
239
+
240
+ ```ruby
241
+ b.machine.zipper.tap do |z|
242
+ w = Stupidedi::Writer::Claredi.new(z.root)
243
+ File.open('output.html', 'w') { |f| f.write w.write }
244
+ end
245
+ ```
246
+
247
+ ### Reading, Traversing
248
+
249
+ ```ruby
250
+ require "tediparse"
251
+
252
+ config = MyApp::EDI.config
253
+ parser = Stupidedi::Parser.build(config)
254
+
255
+ input = File.open("path/to/your.edi", :encoding => "ISO-8859-1")
256
+
257
+ # Reader.build accepts IO (File), String, and DelegateInput
258
+ parser, result = parser.read(Stupidedi::Reader.build(input))
259
+
260
+ # Report fatal tokenizer failures
261
+ if result.fatal?
262
+ result.explain{|reason| raise reason + " at #{result.position.inspect}" }
263
+ end
264
+
265
+ # Helper function: fetch an element from the current segment
266
+ def el(m, *ns, &block)
267
+ if Stupidedi::Either === m
268
+ m.tap{|m| el(m, *ns, &block) }
269
+ else
270
+ yield(*ns.map{|n| m.elementn(n).map(&:value).fetch })
271
+ end
272
+ end
273
+
274
+ parser.first
275
+ .flatmap{|m| m.find(:GS) }
276
+ .flatmap{|m| m.find(:ST) }
277
+ .tap do |m|
278
+ el(m.find(:N1, "PR"), 2){|e| puts "Payer: #{e}" }
279
+ el(m.find(:N1, "PE"), 2){|e| puts "Payee: #{e}" }
280
+ end
281
+ .flatmap{|m| m.find(:LX) }
282
+ .flatmap{|m| m.find(:CLP) }
283
+ .flatmap{|m| m.find(:NM1, "QC") }
284
+ .tap{|m| el(m, 3, 4){|l,f| puts "Patient: #{l}, #{f}" }}
285
+ ```
286
+
287
+ ### Testing
288
+
289
+ ```
290
+ bundle exec rake spec
291
+ ```
292
+
293
+ ## What doesn't it solve?
294
+
295
+ It isn't a translator. It doesn't have bells and whistles, like the
296
+ commercial EDI translators have, so it...
297
+
298
+ - Doesn't convert to/from XML, CSV, etc
299
+ - Doesn't transmit or receive files
300
+ - Doesn't do encryption
301
+ - Doesn't connect to your database
302
+ - Doesn't queue messages for delivery or receipt
303
+ - Doesn't generate acknowledgements
304
+ - Doesn't have a graphical interface
305
+
306
+ These features are orthogonal to the problem tediparse aims to solve, but
307
+ they can certainly be implemented with other code taking advantage of
308
+ tediparse.