X12 0.0.5

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 (50) hide show
  1. data/CHANGELOG +13 -0
  2. data/COPYING +502 -0
  3. data/README +309 -0
  4. data/Rakefile +157 -0
  5. data/TODO +6 -0
  6. data/doc/classes/X12.html +174 -0
  7. data/doc/classes/X12/Base.html +677 -0
  8. data/doc/classes/X12/Composite.html +156 -0
  9. data/doc/classes/X12/Empty.html +186 -0
  10. data/doc/classes/X12/Field.html +339 -0
  11. data/doc/classes/X12/Loop.html +202 -0
  12. data/doc/classes/X12/Parser.html +306 -0
  13. data/doc/classes/X12/Segment.html +277 -0
  14. data/doc/classes/X12/Table.html +198 -0
  15. data/doc/created.rid +1 -0
  16. data/doc/files/CHANGELOG.html +108 -0
  17. data/doc/files/README.html +474 -0
  18. data/doc/files/TODO.html +95 -0
  19. data/doc/files/lib/X12/Base_rb.html +83 -0
  20. data/doc/files/lib/X12/Composite_rb.html +83 -0
  21. data/doc/files/lib/X12/Empty_rb.html +83 -0
  22. data/doc/files/lib/X12/Field_rb.html +83 -0
  23. data/doc/files/lib/X12/Loop_rb.html +83 -0
  24. data/doc/files/lib/X12/Parser_rb.html +83 -0
  25. data/doc/files/lib/X12/Segment_rb.html +83 -0
  26. data/doc/files/lib/X12/Table_rb.html +83 -0
  27. data/doc/files/lib/X12_rb.html +100 -0
  28. data/doc/fr_class_index.html +35 -0
  29. data/doc/fr_file_index.html +38 -0
  30. data/doc/fr_method_index.html +62 -0
  31. data/doc/index.html +27 -0
  32. data/doc/rdoc-style.css +208 -0
  33. data/example/factory.rb +92 -0
  34. data/example/parse.rb +56 -0
  35. data/lib/X12.rb +50 -0
  36. data/lib/X12/Base.rb +192 -0
  37. data/lib/X12/Composite.rb +37 -0
  38. data/lib/X12/Empty.rb +43 -0
  39. data/lib/X12/Field.rb +81 -0
  40. data/lib/X12/Loop.rb +74 -0
  41. data/lib/X12/Parser.rb +98 -0
  42. data/lib/X12/Segment.rb +92 -0
  43. data/lib/X12/Table.rb +44 -0
  44. data/lib/X12/x12syntax.treetop +256 -0
  45. data/misc/997.d12 +885 -0
  46. data/misc/rdoc_template.rb +697 -0
  47. data/test/tc_factory_997.rb +130 -0
  48. data/test/tc_parse_997.rb +146 -0
  49. data/test/ts_x12.rb +27 -0
  50. metadata +108 -0
data/README ADDED
@@ -0,0 +1,309 @@
1
+ = X12Parser - a library to manipulate X12 structures using native Ruby syntax
2
+
3
+ $Id: README 40 2008-11-13 19:51:31Z ikk $
4
+
5
+ *WARNING* <tt>The project is in development. Contributors are welcome.</tt>
6
+
7
+ Project home is at http://rubyforge.org/projects/x12parser/. Please
8
+ note, this is a different project from {Chris Parker's port}[http://rubyforge.org/projects/x12-parser/] of
9
+ {X12::Parser Perl module}[http://search.cpan.org/~prasad/X12-0.09/lib/X12/Parser.pm].
10
+
11
+ == The goal
12
+
13
+ The idea is to access X12 messages directly from Ruby, i.e., using a
14
+ syntax like
15
+
16
+ message.L1000.L1010[1].AK4.DataElementReferenceNumber
17
+
18
+ This syntax can be used to get and set any field of an X12 message and
19
+ it makes X12 parsing much more straightforward and self-documenting.
20
+
21
+ == The problem
22
+
23
+ X12 is a set of "standards" possessing all the elegance of an elephant
24
+ designed by committee, and quite literally so, see http://www.x12.org.
25
+ X12 defines rough syntax for specifying text messages, but each of
26
+ more than 300 specifications defines its own message structure. While
27
+ messages themselves are easy to parse with a simple tokenizer, their
28
+ semantics is heavily dependent on the domain. For example, this is
29
+ X12/997 message conveying "Functional Acknowledgment":
30
+
31
+ ST*997*2878~AK1*HS*293328532~AK2*270*307272179~AK3*NM1*8*L1010_0*8~
32
+ AK4*0:0*66*1~AK4*0:1*66*1~AK4*0:2*66*1~AK3*NM1*8*L1010_1*8~AK4*1:0*
33
+ 66*1~AK4*1:1*66*1~AK3*NM1*8*L1010_2*8~AK4*2:0*66*1~AK5*R*5~AK9*R*1*
34
+ 1*0~SE*8*2878~
35
+
36
+ I.e., X12 defines an alphabet and somewhat of a dictionary - not a
37
+ grammar or semantics for each particular data interchange
38
+ conversation. Because of many entrenched implementations and
39
+ government mandates, the X12 is not going to die anytime soon,
40
+ unfortunately.
41
+
42
+ The message above can be easily represented in Ruby as a nested array:
43
+
44
+ m = [
45
+ ['ST', '997', '2878'],
46
+ ['AK1', 'HS', '293328532'],
47
+ ['AK2', '270', '307272179'],
48
+ ['AK3', 'NM1', '8', 'L1010_0', '8'],
49
+ ['AK4', '0:0', '66', '1'],
50
+ ['AK4', '0:1', '66', '1'],
51
+ ['AK4', '0:2', '66', '1'],
52
+ ['AK3', 'NM1', '8', 'L1010_1', '8'],
53
+ ['AK4', '1:0', '66', '1'],
54
+ ['AK4', '1:1', '66', '1'],
55
+ ['AK3', 'NM1', '8', 'L1010_2', '8'],
56
+ ['AK4', '2:0', '66', '1'],
57
+ ['AK5', 'R', '5'],
58
+ ['AK9', 'R', '1', '1', '0'],
59
+ ['SE', '8', '2878'],
60
+ ]
61
+
62
+ but it will not help any since, say, segment 'AK4' is ambiguously
63
+ defined and its meaning not at all obvious until the message's
64
+ structure is interpreted and correct 'AK4' segment is found.
65
+
66
+ == The solution
67
+
68
+ === Message structure
69
+
70
+ Each participant in EDI has to know the structure of the data coming
71
+ across the wire - X12 or no X12. The X12 structures are defined in
72
+ so-called Implementation Guides - thick books with all the data pieces
73
+ spelled out. There is no other choice, but to invent a
74
+ computer-readable definition language that will codify these
75
+ books. For example, the X12/997 message can be defined as
76
+
77
+ loop 997 1:1
78
+ {
79
+ segment ST 1:1
80
+ segment AK1 1:1
81
+ loop L1000 0:999999
82
+ {
83
+ segment AK2 0:1
84
+ loop L1010 0:999999
85
+ {
86
+ segment AK3 0:1
87
+ segment AK4 0:99
88
+ } # L1010
89
+ segment AK5 1:1
90
+ } # L1000
91
+ segment AK9 1:1
92
+ segment SE 1:1
93
+ } # 997
94
+
95
+ Namely, the 997 is a 'loop' containing segments ST (only one - '1:1'),
96
+ AK1 (also only one), another loop L1000 (zero or many repeats),
97
+ segments AK9 and SE. The loop L1000 can contain a segment AK2
98
+ (optional - '0:1') and another loop L1010 (zero or many), and so on.
99
+
100
+ The segments' structure can be further defined as, for example,
101
+
102
+ segment AK2 {
103
+ TransactionSetIdentifierCode S R 3-3 Tbl143
104
+ TransactionSetControlNumber S R 4-9
105
+ } # AK2
106
+
107
+ wihch defines a segment AK2 as having to fields:
108
+ TransactionSetIdentifierCode and TransactionSetControlNumber. The
109
+ field TransactionSetIdentifierCode is defined as having a type of
110
+ string ('S'), begin required ('R'), having length of minimum 3 and
111
+ maximum 3 characters ('3-3'), and being validated against a table
112
+ Tbl143. The validation table is defined as
113
+
114
+ table Tbl143 {
115
+ 100 Insurance Plan Description
116
+ 101 Name and Address Lists
117
+ ...
118
+ 997 Functional Acknowledgment
119
+ 998 Set Cancellation
120
+ } # Tbl143
121
+
122
+ where required values are first tokens on each line, i.e., 100, 101,
123
+ ..., 997, 998.
124
+
125
+ This message is fully flashed out in an example 'misc/997.d12' file,
126
+ copied from the ASC X12N 276/277 (004010X093) "Health Care
127
+ Claim Status Request and Response" National Electronic Data
128
+ Interchange Transaction Set Implementation Guide.
129
+
130
+ Now expressions like
131
+
132
+ message.L1000.L1010[1].AK4.DataElementReferenceNumber
133
+
134
+ start making sense of sorts, overall X12's idiocy notwithstanding - it's
135
+ a field called 'DataElementReferenceNumber' of a first of possibly
136
+ many segments 'AK4' found in the second repeat of the loop 'L1010'
137
+ inside the enclosing loop 'L1000'. The meaning of the value '66' found
138
+ in this field is still in the eye of the beholder, but, at least its
139
+ location is clearly identified in the message.
140
+
141
+
142
+ === X12 Structure Definition Language (d12)
143
+
144
+ The syntax of the X12 structure definition language should be apparent
145
+ from the '997.d12' file enclosed with the package. The strict definition
146
+ is formalized in 'lib/X12/x12syntax.treetop' file.
147
+
148
+ === Parsing
149
+
150
+ Here is how to parse an X12/997 message (the source is in
151
+ example/parse.rb):
152
+
153
+ require 'x12'
154
+
155
+ # Read message definition and create an actual parser
156
+ # by compiling .d12 file
157
+ parser = X12::Parser.new('misc/997.d12')
158
+
159
+ # Define a test message to parse
160
+ m997='ST*997*2878~AK1*HS*293328532~AK2*270*307272179~'\
161
+ 'AK3*NM1*8*L1010_0*8~AK4*0:0*66*1~AK4*0:1*66*1~AK4*0:2*'\
162
+ '66*1~AK3*NM1*8*L1010_1*8~AK4*1:0*66*1~AK4*1:1*66*1~AK3*'\
163
+ 'NM1*8*L1010_2*8~AK4*2:0*66*1~AK5*R*5~AK9*R*1*1*0~SE*8*2878~'
164
+
165
+ # Parse the message
166
+ r = parser.parse('997', m997)
167
+
168
+ # Access components of the message as desired
169
+
170
+ # Whole ST segment: -> ST*997*2878~
171
+ puts r.ST
172
+
173
+ # One filed, Group Control Number of AK1 -> 293328532
174
+ puts r.AK1.GroupControlNumber
175
+
176
+ # Individual loop, namely, third L1010 sub-loop of
177
+ # L1000 loop: -> AK3*NM1*8*L1010_2*8~AK4*2:0*66*1~
178
+ puts r.L1000.L1010[2]
179
+
180
+ # First encounter of Data Element Reference Number of the
181
+ # first L1010 sub-loop of L1000 loop -> 66
182
+ puts r.L1000.L1010.AK4.DataElementReferenceNumber
183
+
184
+ # Number of L1010 sub-loops in L1000 loop -> 3
185
+ puts r.L1000.L1010.size
186
+
187
+ === Generating
188
+
189
+ Here is how to perform a reverse operation and generate a well-formed
190
+ 997 message (the source is in example/factory.rb):
191
+
192
+ require 'x12'
193
+
194
+ # Read message definition and create an actual parser
195
+ # by compiling .d12 file
196
+ parser = X12::Parser.new('misc/997.d12')
197
+
198
+ # Make a new 997 message
199
+ r = parser.factory('997')
200
+
201
+ #
202
+ # Set various fields as desired
203
+ #
204
+
205
+ # Set fields directly
206
+ r.ST.TransactionSetIdentifierCode = 997
207
+ r.ST.TransactionSetControlNumber = '2878'
208
+
209
+ # Set fields inside a segment (AK1 in this case)
210
+ r.AK1 { |ak1|
211
+ ak1.FunctionalIdentifierCode = 'HS'
212
+ ak1.GroupControlNumber = 293328532
213
+ }
214
+
215
+ # Set fields deeply inside a segment inside
216
+ # nested loops (L1000/L1010/AK4 in this case)
217
+ r.L1000.L1010.AK4.DataElementSyntaxErrorCode = 55
218
+ r.L1000.AK2.TransactionSetIdentifierCode = 270
219
+
220
+ # Set nested loops
221
+ r.L1000.L1010 {|l|
222
+ l.AK3 {|s|
223
+ s.SegmentIdCode = 'NM1'
224
+ s.LoopIdentifierCode = 'L1000D'
225
+ }
226
+ l.AK4 {|s|
227
+ s.CopyOfBadDataElement = 'Bad element'
228
+ }
229
+ }
230
+
231
+ # Add loop repeats
232
+ r.L1000.repeat {|l1000|
233
+ (0..1).each {|loop_repeat| # Two repeats of the loop L1010
234
+ l1000.L1010.repeat {|l1010|
235
+ l1010.AK3 {|s|
236
+ s.SegmentIdCode = 'DMG'
237
+ s.SegmentPositionInTransactionSet = 0
238
+ s.LoopIdentifierCode = 'L1010'
239
+ s.SegmentSyntaxErrorCode = 22
240
+ } if loop_repeat == 0 # AK3 only in the first repeat of L1010
241
+ (0..1).each {|ak4_repeat| # Two repeats of the segment AK4
242
+ l1010.AK4.repeat {|s|
243
+ s.PositionInSegment = loop_repeat
244
+ s.DataElementSyntaxErrorCode = ak4_repeat
245
+ } # s
246
+ } # ak4_repeat
247
+ } # l1010
248
+ } # loop_repeat
249
+
250
+ l1000.AK5{|a|
251
+ a.TransactionSetAcknowledgmentCode = 666
252
+ a.TransactionSetSyntaxErrorCode4 = 999
253
+ } # a
254
+ } # l1000
255
+
256
+ # Print the message as a string -> ST*997*2878~AK1*HS*293328532~
257
+ # AK2*270*~AK3*NM1**L1000D~AK4***55*Bad element~AK5*~AK3*DMG*0*
258
+ # L1010*22~AK4*0**0~AK4*0**1~AK4*1**0~AK4*1**1~AK5*666****999~
259
+ # AK9****~SE**~
260
+ puts r.render
261
+
262
+ == Download
263
+
264
+ The latest X12 library version can be downloaded from
265
+ http://rubyforge.org/frs/?group_id=7297
266
+
267
+ == Installation
268
+
269
+ You can install X12 library with the following command.
270
+
271
+ % gem install X12
272
+
273
+ If you install directly from the X12*.gem file, it requires these
274
+ packages to be installed first:
275
+ * {Treetop}[http://rubyforge.org/projects/treetop/]
276
+ * {Polyglot}[http://rubyforge.org/projects/polyglot/]
277
+
278
+ == License
279
+
280
+ X12 library is released under the Lesser GPL license, see
281
+ http://www.gnu.org/licenses/lgpl.txt
282
+
283
+ == Major deficiencies
284
+
285
+ * Validation is not implemented.
286
+ * Field types and sizes are ignored.
287
+ * No access methods for composites' fields.
288
+
289
+ == Wish list
290
+
291
+ * .d12 files should have an 'include' facility, so data definitions can be reused for different messages.
292
+
293
+ * It would be nice to codify all popular X12 messages in .d12 format.
294
+
295
+ == Support
296
+
297
+ Please use the following:
298
+
299
+ * forums on Rubyforge for general discussions, http://rubyforge.org/forum/?group_id=7297
300
+ * trackers to submit bugs or feature requests, http://rubyforge.org/tracker/?group_id=7297
301
+ * to contact the author, send mail to prelude rubyforge org
302
+
303
+ == Acknowledgments
304
+
305
+ The authors of the project were inspired by the following works:
306
+
307
+ 1. The Perl X12 parser by Prasad Poruporuthan, http://search.cpan.org/~prasad/X12-0.09/lib/X12/Parser.pm
308
+ 2. The Ruby port of the above by Chris Parker, http://rubyforge.org/projects/x12-parser/
309
+ 3. Treetop Ruby parser, http://treetop.rubyforge.org
@@ -0,0 +1,157 @@
1
+ #--
2
+ # This file is part of the X12Parser library that provides tools to
3
+ # manipulate X12 messages using Ruby native syntax.
4
+ #
5
+ # http://x12parser.rubyforge.org
6
+ #
7
+ # Copyright (C) 2008 APP Design, Inc.
8
+ #
9
+ # This library is free software; you can redistribute it and/or
10
+ # modify it under the terms of the GNU Lesser General Public
11
+ # License as published by the Free Software Foundation; either
12
+ # version 2.1 of the License, or (at your option) any later version.
13
+ #
14
+ # This library is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
+ # Lesser General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU Lesser General Public
20
+ # License along with this library; if not, write to the Free Software
21
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
+ #
23
+ # $Id: Rakefile 39 2008-11-13 19:43:55Z ikk $
24
+ #++
25
+ require 'rubygems'
26
+ require 'rake'
27
+ require 'rake/clean'
28
+ require 'rake/testtask'
29
+ require 'rake/rdoctask'
30
+ require 'rake/packagetask'
31
+ require 'rake/gempackagetask'
32
+ require 'rake/contrib/rubyforgepublisher'
33
+ require 'fileutils'
34
+ require 'pp'
35
+
36
+ require File.join(File.dirname(__FILE__), 'lib', 'X12')
37
+
38
+ PKG_NAME = 'X12'
39
+ PKG_VERSION = X12::VERSION
40
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
41
+ PKG_DESTINATION = "../#{PKG_NAME}"
42
+
43
+ RAKE = $0
44
+ RUBY_DIR = File.expand_path(File.dirname(RAKE)+'../..')
45
+ RUBY = "#{RUBY_DIR}/bin/ruby.exe"
46
+
47
+ CLEAN.include(
48
+ '**/*.log',
49
+ 'doc/**/*',
50
+ '**/*.tmp',
51
+ 'lib/X12/X12syntax.rb'
52
+ )
53
+ CLEAN.exclude(
54
+ )
55
+
56
+
57
+ #puts "Files to clobber: #{CLOBBER}"
58
+ #puts "Files to clean: #{CLEAN}"
59
+
60
+ desc "Default Task"
61
+ task :default => [ :example, :test ]
62
+
63
+ # Run examples
64
+ task :example do |x|
65
+ Dir['example/*.rb'].each {|f|
66
+ puts "Running #{f}"
67
+ sh(RUBY, '-I', 'lib', f) do |ok, res|
68
+ fail "Command failed (status = #{res.exitstatus})" unless ok
69
+ end
70
+ }
71
+ end
72
+
73
+ task :test do |x|
74
+ Rake::TestTask.new { |t|
75
+ t.libs << "test"
76
+ t.test_files = Dir['test/ts_*.rb']
77
+ t.verbose = true
78
+ }
79
+ end # :test
80
+
81
+ file 'doc/index.html' => ['misc/rdoc_template.rb' ]
82
+ task :rdoc => ['doc/index.html']
83
+
84
+ # Generate the RDoc documentation
85
+ Rake::RDocTask.new { |rdoc|
86
+ rdoc.rdoc_dir = 'doc'
87
+ rdoc.title = "X12 -- an X12 parsing library"
88
+ rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
89
+ rdoc.template = 'misc/rdoc_template.rb'
90
+ rdoc.rdoc_files.include('README')
91
+ rdoc.rdoc_files.include('CHANGELOG')
92
+ rdoc.rdoc_files.include('TODO')
93
+ rdoc.rdoc_files.include('lib/**/*.rb')
94
+ }
95
+
96
+
97
+ # Create compressed packages by running 'rake gem'
98
+
99
+ desc "gem task"
100
+ task :gem => [ :clobber, :rdoc ]
101
+
102
+ # Here's how to look inside the gem package:
103
+ # ~..>tar xvf package.gem; gunzip data.tar.gz metadata.gz; tar xvf data.tar; cat metadata
104
+ spec = Gem::Specification.new do |s|
105
+ s.platform = Gem::Platform::RUBY
106
+ s.name = PKG_NAME
107
+ s.version = PKG_VERSION
108
+ s.summary = 'X12 parsing and generation library'
109
+ s.description = 'Library to parse X12 messages and manipulate their loops, segments, fields, composites, and validation tables.'
110
+ s.author = 'APP Design, Inc.'
111
+ s.email = 'info@appdesign.com'
112
+ s.rubyforge_project = PKG_NAME
113
+ s.homepage = "http://www.appdesign.com"
114
+
115
+ s.has_rdoc = true
116
+ s.requirements << 'none'
117
+ s.require_path = 'lib'
118
+ s.autorequire = 'x12'
119
+ s.add_dependency "treetop"
120
+
121
+ [
122
+ "Rakefile",
123
+ "README",
124
+ "TODO",
125
+ "CHANGELOG",
126
+ "COPYING",
127
+ "doc/**/*",
128
+ "lib/**/*",
129
+ "test/**/*",
130
+ "example/**/*",
131
+ "misc/**/*"
132
+ ].each do |i|
133
+ s.files += Dir.glob(i).delete_if do
134
+ |x| x =~ /.*~/
135
+ end
136
+ end
137
+ puts "Files included into the GEM:" if $VERBOSE
138
+ pp s.files if $VERBOSE
139
+
140
+ s.test_files = Dir.glob( "test/**/ts_*rb" )
141
+ end
142
+
143
+ Rake::GemPackageTask.new(spec) do |p|
144
+ p.gem_spec = spec
145
+ p.need_tar = true
146
+ p.need_zip = true
147
+ end
148
+
149
+ # Compile treetop definition into Ruby code. Like this:
150
+ # >rake lib/X12/X12syntax.rb
151
+ # It's needed only for debugging Treetop's internals.
152
+ rule '.rb' => ['.treetop'] do |t|
153
+ require 'treetop'
154
+ compiler = Treetop::Compiler::GrammarCompiler.new
155
+ puts "Compiling [#{t.source}]"
156
+ compiler.compile(t.source)
157
+ end