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
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.
|