X12 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
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