edi4r 0.9.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/AuthorCopyright +10 -0
  2. data/COPYING +56 -0
  3. data/ChangeLog +106 -0
  4. data/README +66 -0
  5. data/TO-DO +35 -0
  6. data/Tutorial +609 -0
  7. data/VERSION +1 -0
  8. data/bin/edi2xml.rb +103 -0
  9. data/bin/editool.rb +151 -0
  10. data/bin/xml2edi.rb +50 -0
  11. data/data/edifact/iso9735/SDCD.10000.csv +10 -0
  12. data/data/edifact/iso9735/SDCD.20000.csv +10 -0
  13. data/data/edifact/iso9735/SDCD.30000.csv +11 -0
  14. data/data/edifact/iso9735/SDCD.40000.csv +31 -0
  15. data/data/edifact/iso9735/SDCD.40100.csv +31 -0
  16. data/data/edifact/iso9735/SDED.10000.csv +37 -0
  17. data/data/edifact/iso9735/SDED.20000.csv +37 -0
  18. data/data/edifact/iso9735/SDED.30000.csv +43 -0
  19. data/data/edifact/iso9735/SDED.40000.csv +129 -0
  20. data/data/edifact/iso9735/SDED.40100.csv +130 -0
  21. data/data/edifact/iso9735/SDMD.10000.csv +0 -0
  22. data/data/edifact/iso9735/SDMD.20000.csv +0 -0
  23. data/data/edifact/iso9735/SDMD.30000.csv +6 -0
  24. data/data/edifact/iso9735/SDMD.40000.csv +17 -0
  25. data/data/edifact/iso9735/SDMD.40100.csv +17 -0
  26. data/data/edifact/iso9735/SDSD.10000.csv +8 -0
  27. data/data/edifact/iso9735/SDSD.20000.csv +8 -0
  28. data/data/edifact/iso9735/SDSD.30000.csv +12 -0
  29. data/data/edifact/iso9735/SDSD.40000.csv +34 -0
  30. data/data/edifact/iso9735/SDSD.40100.csv +34 -0
  31. data/data/edifact/untdid/EDCD.d01b.csv +200 -0
  32. data/data/edifact/untdid/EDCD.d96a.csv +161 -0
  33. data/data/edifact/untdid/EDED.d01b.csv +641 -0
  34. data/data/edifact/untdid/EDED.d96a.csv +462 -0
  35. data/data/edifact/untdid/EDMD.d01b.csv +3419 -0
  36. data/data/edifact/untdid/EDMD.d96a.csv +2144 -0
  37. data/data/edifact/untdid/EDSD.d01b.csv +158 -0
  38. data/data/edifact/untdid/EDSD.d96a.csv +127 -0
  39. data/data/edifact/untdid/IDCD.d01b.csv +95 -0
  40. data/data/edifact/untdid/IDMD.d01b.csv +238 -0
  41. data/data/edifact/untdid/IDSD.d01b.csv +75 -0
  42. data/lib/edi4r.rb +928 -0
  43. data/lib/edi4r/diagrams.rb +567 -0
  44. data/lib/edi4r/edi4r-1.2.dtd +20 -0
  45. data/lib/edi4r/edifact-rexml.rb +221 -0
  46. data/lib/edi4r/edifact.rb +1627 -0
  47. data/lib/edi4r/rexml.rb +256 -0
  48. data/lib/edi4r/standards.rb +495 -0
  49. data/test/eancom2webedi.rb +380 -0
  50. data/test/groups.edi +1 -0
  51. data/test/in1.edi +1 -0
  52. data/test/in1.inh +3 -0
  53. data/test/in2.edi +1 -0
  54. data/test/in2.xml +350 -0
  55. data/test/test_basics.rb +209 -0
  56. data/test/test_edi_split.rb +53 -0
  57. data/test/test_loopback.rb +21 -0
  58. data/test/test_minidemo.rb +84 -0
  59. data/test/test_rexml.rb +98 -0
  60. data/test/test_tut_examples.rb +131 -0
  61. data/test/webedi2eancom.rb +408 -0
  62. metadata +110 -0
@@ -0,0 +1,10 @@
1
+ == Author
2
+
3
+ Heinz W. Werntges, FH Wiesbaden
4
+ (edi@informatik.fh-wiesbaden.de)
5
+
6
+ == Copyright
7
+
8
+ Copyright (c) 2006 Heinz W. Werntges.
9
+ Licensed under the same terms as Ruby.
10
+
data/COPYING ADDED
@@ -0,0 +1,56 @@
1
+ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
2
+ You can redistribute it and/or modify it under either the terms of the GPL
3
+ (see the file GPL), or the conditions below:
4
+
5
+ 1. You may make and give away verbatim copies of the source form of the
6
+ software without restriction, provided that you duplicate all of the
7
+ original copyright notices and associated disclaimers.
8
+
9
+ 2. You may modify your copy of the software in any way, provided that
10
+ you do at least ONE of the following:
11
+
12
+ a) place your modifications in the Public Domain or otherwise
13
+ make them Freely Available, such as by posting said
14
+ modifications to Usenet or an equivalent medium, or by allowing
15
+ the author to include your modifications in the software.
16
+
17
+ b) use the modified software only within your corporation or
18
+ organization.
19
+
20
+ c) give non-standard binaries non-standard names, with
21
+ instructions on where to get the original software distribution.
22
+
23
+ d) make other distribution arrangements with the author.
24
+
25
+ 3. You may distribute the software in object code or binary form,
26
+ provided that you do at least ONE of the following:
27
+
28
+ a) distribute the binaries and library files of the software,
29
+ together with instructions (in the manual page or equivalent)
30
+ on where to get the original distribution.
31
+
32
+ b) accompany the distribution with the machine-readable source of
33
+ the software.
34
+
35
+ c) give non-standard binaries non-standard names, with
36
+ instructions on where to get the original software distribution.
37
+
38
+ d) make other distribution arrangements with the author.
39
+
40
+ 4. You may modify and include the part of the software into any other
41
+ software (possibly commercial). But some files in the distribution
42
+ are not written by the author, so that they are not under these terms.
43
+
44
+ For the list of those files and their copying conditions, see the
45
+ file LEGAL.
46
+
47
+ 5. The scripts and library files supplied as input to or produced as
48
+ output from the software do not automatically fall under the
49
+ copyright of the software, but belong to whomever generated them,
50
+ and may be sold commercially, and may be aggregated with this
51
+ software.
52
+
53
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
54
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
55
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56
+ PURPOSE.
@@ -0,0 +1,106 @@
1
+ = Change log
2
+
3
+ == 0.9.4.1
4
+
5
+ === News
6
+ * XML support added (again; no separate module anymore)!
7
+ * DIN 16557-4 now supported (one-way, EDI-to-XML)
8
+ * New standalone tools: edi2xml.rb, xml2edi.rb
9
+ * New testcase: test_rexml.rb
10
+ * EDI::Interchange: New convenience methods: parse(), peek(), detect()
11
+ * Support for compressed data (gzip via zlib, bzip2 if "bzcat" available)
12
+ * peek(): High-speed access to (just the) Interchange header data
13
+
14
+ === Changes
15
+ * Some optional calling parameters added
16
+ * editool.rb: Now supporting more standards, compression, and peek mode/reports
17
+
18
+ === Misc improvements & bug fixes
19
+ * Bug in EDIFACT scanner fixed - scanner re-implemented
20
+ * Windows supported now (workaround for issue with Pathname#realpath).
21
+ Note: Put "ruby" in front of edi4r scripts & tools when invoked
22
+ in pipe mode in a DOS shell, or the pipe fill fail.
23
+
24
+ == 0.9.4.0
25
+ === New structures
26
+ * Time: Class method "edifact", method "format" added
27
+ * MsgGroup: Now fully supported
28
+
29
+ === Changes
30
+ * New test case "test_minidemo" aimed at fixes in DE#to_s, see below
31
+ * "test_basics" now covering MsgGroup tests as well
32
+ * Special setters de0020=, de0048=, de0062=, de0340= added
33
+ to make header/trailer changes consistent.
34
+ * Setters de0001=, de0002= added to prevent changes to charset and version
35
+ * E::Message#validate: Improved
36
+ * Tests updated to reflect new features
37
+
38
+ === Misc improvements & bug fixes
39
+ * E::Interchange.parse: hnd.close removed; some code cleanup
40
+ * EDI::MsgGroup: Completed
41
+ * EDI::E::MsgGroup: Completed, now fully supported
42
+ * E::DE#to_s: Fix - now calling super()
43
+ * DE#to_s: Fix - adding leading zeroes for too short numeric values
44
+ * DE#validate: No more warnings if format e.g. n6 and fixable
45
+ * E::Illegal_Charset_Patterns: Bugfix ('+' missing, '*' included twice)
46
+
47
+
48
+ == 0.9.3.1
49
+ Bugfix release, now integrated in 0.9.4
50
+
51
+ === New structures
52
+ * (none so far)
53
+
54
+ === Changes
55
+ * New test case "test_minidemo" aimed at fixes in DE#to_s, see below
56
+
57
+ === Misc improvements & bug fixes
58
+ * E::Interchange.parse: hnd.close removed; some code cleanup
59
+ * E::DE#to_s: Fix - now calling super()
60
+ * DE#to_s: Fix - adding leading zeroes for too short numeric values
61
+ * DE#validate: No more warnings if format e.g. n6 and fixable
62
+
63
+
64
+ == 0.9.3
65
+ === New structures
66
+ * Improved class hierarchy
67
+ * Methods: More consistent, better inheritance
68
+ * Removed: write() (use to_s !)
69
+ * inspect(): more attributes for segments
70
+ * EDI::E::UNA now bundles all UNA related methods
71
+
72
+ === Changes
73
+ * New access to DE/CDE arrays (like ) through prefix 'a'
74
+ prevents users from accidentally overwriting a DE.
75
+ Previous access through cCxxx[] or dxxxx[] will fail now.
76
+ * Some parameters of EDI::E::Interchange.new have changed,
77
+ several more added.
78
+ * UNA handling: Now through attributes of UNA class.
79
+
80
+ === Misc improvements
81
+ * Improved handling of both decimal signs
82
+ * Bug removed in escaping of special chars
83
+ * Refactoring of some internals
84
+ * RDoc now usable - completely overhauled.
85
+
86
+ == 0.9.2
87
+ === Misc improvements
88
+ * New parsing basics: "String::separate" replaced by EDI::E::edi_split
89
+ * Parsing bug removed
90
+ * More validation features
91
+ * "inspect", "find_all" added
92
+ * "descendants_and_self" etc. added to EDI::Segment
93
+ * bin/editool.rb added (validate, list, inspect EDI data)
94
+
95
+ == 0.9.1
96
+ === First release as a gem
97
+ * Split from the edi4r core package, turned into a separate one.
98
+ * Unit tests added
99
+ * RDoc documentation added
100
+ * Modular loading scheme for normdata added (through EDI_NDB_PATH)
101
+ * Parameter usage overhauled
102
+ * Rolled into a gem
103
+
104
+ == 0.8.x
105
+ * Used internally for projects and teaching
106
+
data/README ADDED
@@ -0,0 +1,66 @@
1
+ = EDI TOOLKIT for RUBY (edi4r)
2
+
3
+ This is Ruby gem <b>edi4r</b> version
4
+ :include:VERSION
5
+
6
+ Edi4r was created to greatly simplify the creation and processing of data for
7
+ Electronic Data Interchange (EDI). In particular, it supports the UN/EDIFACT
8
+ syntax (ISO 9573) and optionally SAP IDocs.
9
+
10
+ == Installation
11
+
12
+ Install it as any other Ruby gem, e.g.:
13
+
14
+ sudo gem install edi4r-<version>.gem
15
+
16
+ == Usage
17
+
18
+ require 'rubygems'
19
+ require_gem 'edi4r'
20
+ require 'edi4r/edifact' # optional
21
+
22
+ # Build a UN/EDIFACT interchange from its character representation in a file:
23
+
24
+ ic = nil
25
+ File.open("received.edi") {|hnd| ic = EDI::E::Interchange.parse( hnd ) }
26
+ ic.each do |msg|
27
+ # Process message, here: Just list document numbers from (only) segment BGM
28
+ puts msg['BGM'].first.d1004
29
+ end
30
+
31
+ # Create a minimalistic interchange
32
+
33
+ ic = EDI::E::Interchange.new # Default: syntax version=3, charset = UNOB
34
+ msg = ic.new_message # Default: ORDERS D.96A
35
+
36
+ bgm = msg.new_segment('BGM') # Obtain an empty segment
37
+ bgm.cC002.d1001 = '220' # Add some content to mandatory elements
38
+ bgm.d1004 = 'PO-54321'
39
+ dtm = msg.new_segment('DTM')
40
+ dtm.cC507.d2005 = '137'
41
+ uns = msg.new_segment('UNS')
42
+ uns.d0081 = 'S'
43
+ [bgm, dtm, uns].each {|seg| msg.add seg} # Add segments to message
44
+ ic.add msg # Add message to interchange - ready to go!
45
+
46
+ ic.header.cS002.d0004 = 'sender'; ic.header.cS003.d0010 = 'recipient' # UNB
47
+ ic.validate # Conforming to standard?
48
+ print ic # Could be sent that way!
49
+
50
+ == See also
51
+
52
+ * Background[link:files/lib/edi4r_rb.html] info about data structure and classes.
53
+
54
+ * A Tutorial[link:files/Tutorial.html] for examples of use.
55
+
56
+ * A ChangeLog[link:files/ChangeLog.html] will be maintained; it is just starting now.
57
+
58
+ * Finally, see TO-DO[link:files/TO-DO.html] for the current wish list.
59
+
60
+ * This code is put under the Ruby license, see COPYING[link:files/COPYING.html] for details.
61
+
62
+ :include:AuthorCopyright
63
+
64
+ Enjoy,
65
+
66
+ Heinz
data/TO-DO ADDED
@@ -0,0 +1,35 @@
1
+ = To-do list
2
+
3
+ == Documentation
4
+ * Further improve RDoc support
5
+ * DocBook based tutorial
6
+ * FAQ section
7
+ * Intro to EDI in general
8
+ * Outlook: More components (of a real EDI/EAI server), e.g. for messaging
9
+
10
+ == XML support
11
+ * DTD generation from Diagrams
12
+
13
+ == Core features
14
+ * E: more SV4 support, I-EDI support
15
+ * E: Support for code lists, subsets, restricted codelists
16
+ * Recovery from parsing errors, ability to skip faulty messages
17
+ * Consider a "C" extension for fast segmentation of plain EDIFACT data
18
+ * Generic support for CSV/variable-length and fixed record formats?
19
+ * Add a generic number generator
20
+ * Add EAN/GTIN/EPC helpers
21
+
22
+ == XPath support
23
+ * method "each" accepting XPath parameter
24
+ * Index op [] accepting XPath
25
+ * More XPath expressions + corresponding methods
26
+
27
+ == Applications
28
+ * Better list mode in editool.rb
29
+
30
+
31
+ == Test suite
32
+ * Extend
33
+
34
+ == Details
35
+ * Avoid module name 'Dir' - might collide with std. classname 'Dir'
@@ -0,0 +1,609 @@
1
+ = Tutorial
2
+
3
+ == Getting started
4
+
5
+ === Installation
6
+
7
+ sudo gem install edi4r
8
+ sudo gem install edi4r-tdid
9
+
10
+ === Require statements
11
+
12
+ require "rubygems"
13
+ require "edi4r" # Try require_gem if this fails on your site
14
+ require "edi4r/edifact"
15
+ require "edi4r-tdid" # Try require_gem if this fails on your site
16
+
17
+
18
+ == Creating an (outbound) UN/EDIFACT interchange
19
+
20
+ === An empty interchange
21
+
22
+ ic = EDI::E::Interchange.new
23
+
24
+ creates an empty interchange object with syntax version 3
25
+ and charset UNOB. You can make this a bit more explicit
26
+ by passing parameters as hash components:
27
+
28
+ ic = EDI::E::Interchange.new( :version => 3, :charset => 'UNOB' )
29
+
30
+ See the source for more parameters.
31
+
32
+ === An empty message
33
+
34
+ msg = ic.new_message
35
+
36
+ creates an empty message in the context of the given interchange,
37
+ i.e. the syntax version, charset, UNA settings, interactive or batch EDI.
38
+
39
+ By default, the message type is <tt>ORDERS D.96A</tt>. Select any
40
+ message from any UN/TDID by passing the corresponding parameters
41
+ as hash components:
42
+
43
+ msg = ic.new_message( :msg_type=>'ORDERS', :version=>'D', :release=>'96A',
44
+ :resp_agency=>'UN' )
45
+
46
+ Hash components which you do not specify are taken from a set of defaults.
47
+
48
+
49
+ === Filling an interchange
50
+
51
+ You may add messages to the interchange any time by calling method add():
52
+
53
+ ic.add( msg )
54
+
55
+ When adding new messages to an interchange, they get appended to the
56
+ current interchange content. There is no method to insert a message
57
+ at any other location. If you need to do that, hold your messages
58
+ in an array, sort them any way you like, and finally add them
59
+ to the interchange in the desired sequence.
60
+
61
+ Note that each messag gets validated by default when you add it to
62
+ the interchange. If your message needs to be completed only later,
63
+ you may disable validation by calling:
64
+
65
+ ic.add( msg, false )
66
+
67
+ === Filling a message
68
+
69
+ A freshly created message is empty, aside from its header and trailer
70
+ which we shall discuss later. Simply create the segments you want to add,
71
+ fill them, and add them to the message:
72
+
73
+ seg = msg.new_segment( 'BGM' )
74
+
75
+ Here, we derived a BGM segment from the current context,
76
+ i.e. an UN/TDID like D.96A which we specified when creating the message given.
77
+
78
+ Note that <tt>new_segment()</tt> accepts all segment tags available in the
79
+ whole TDID's segment directory - not just those usable within
80
+ this message type.
81
+
82
+ Add content to the segment (see below) and add it to the message:
83
+
84
+ msg.add( seg )
85
+
86
+ Like with messages added to an interchange, it is your responsibility
87
+ to assure the proper sequence of segments. You will need the UN/EDIFACT
88
+ message structure, a subset description, or a message implementation
89
+ guideline (MIG) handy in order to comply.
90
+
91
+ It is possible to add empty or partially filled segments to a message.
92
+ Just keep a reference to them and fill in their required data elements later.
93
+
94
+
95
+ == Accessing Composites and Data Elements
96
+
97
+ === Background
98
+
99
+ While interchanges and messages are basically empty when created,
100
+ segments are not: They come equipped with the composites (CDE, composite
101
+ data elements ) and data elements (DE) they comprise in their current
102
+ context. Likewise, CDEs come equipped with the sequence of DEs
103
+ which they contain according to the underlying TDID.
104
+
105
+ Segments and CDEs are basically sequences (arrays) of their component
106
+ elements. This sequence depends on the TDID of their context.
107
+ E.g., a BGM segment from D.96A looks different from a BGM in D.01B.
108
+ These sequences are fixed and cannot/should not be altered after
109
+ creation of a segment or CDE.
110
+
111
+ === Getters for CDE
112
+
113
+ There is a getter method for each CDE of a segment.
114
+ Its name is simply prefix 'c' and the CDE name.
115
+
116
+ In order to access, say, C002 in segment BGM, the getter
117
+ is named 'c' + 'C002':
118
+
119
+ cde = seg.cC002
120
+
121
+ (See below how to handle arrays)
122
+
123
+ In most cases you'll need the CDE object only to access
124
+ its component data elements. Let's see how that works:
125
+
126
+ === Getters for DE values
127
+
128
+ During mapping tasks, we normally do not deal with the
129
+ internal organization of DE objects. All we need is access
130
+ to their <em>values</em>.
131
+
132
+ Similarly to CDE getters, we build a DE getter by prepending
133
+ a 'd' to the DE name. The result is a method that returns
134
+ the current value of this DE (nil if unassigned), not the
135
+ DE object itself:
136
+
137
+ order_number = seg.d1004 if cde.d1001 == 220
138
+
139
+ The example shows that this concept is very convenient and works
140
+ both with component DEs in a CDE and plain DEs in a segment.
141
+
142
+ === Setters for DE values
143
+
144
+ We use the same approach for setters - a DE setter actually
145
+ changes its value, not the DE object itself:
146
+
147
+ bgm = msg.new_segment( 'BGM' )
148
+ bgm.d1004 = '123456ABC'
149
+ bgm.cC002.d1001 = 220
150
+
151
+ Well, that's both easy and readable! But what about the
152
+ integer assigned to DE 1004? Don't worry - its string value
153
+ is still what we later want to see in the interchange file.
154
+
155
+ === Setters for CDE and segments?
156
+
157
+ There is no such thing! A CDE should always be derived
158
+ from its proper context and must not be changed thereafter.
159
+ Likewise, a segment's content is nothing a user should
160
+ interfere with.
161
+
162
+ Does that sound like dictatorship? Well, there *are* ways
163
+ to manipulate CDEs and segments, but that's an advanced
164
+ and rarely used topic well out of scope of this tutorial ...
165
+
166
+ === DE and CDE arrays
167
+
168
+ Sometimes, a DE occurs multiple times within a segment or CDE,
169
+ and a CDE may occur multiple times within a segment.
170
+
171
+ Before syntax version 4, EDIFACT does not really employ
172
+ the concept of an array. Instead, there are multiple
173
+ occurrences of a particular DE or CDE listed in a row.
174
+
175
+ In such a case, the corresponding getters and setters
176
+ won't work. Actually, they raise a TypeError to make sure
177
+ that you don't accidentally overlook that there is more
178
+ than one instance of the given (C)DE.
179
+
180
+ We obtain a whole array of *all* matching (C)DE instead
181
+ and indicate this with a prefix 'a':
182
+
183
+ seg = msg.new_segment('PIA')
184
+ cde_list = seg.aC212
185
+
186
+ cde_list[0].d7140 = '54321'
187
+ cde_list[0].d7143 = 'SA'
188
+ cde_list[0].d3055 = 91
189
+
190
+ cde_list[1].d7140 = ... # etc
191
+
192
+ seg = msg.new_segment('NAD')
193
+ seg.cC080.a3036[0].value = 'E. X. Ample'
194
+ seg.cC080.a3036[1].value = 'Sales dept.'
195
+
196
+ Note that a3036 returns an array of DE objects, not their values!
197
+ We thus use DE setter <tt>value</tt> to actually assign
198
+ new values to those DE objects.
199
+
200
+ Sometimes, a CDE or segment contains the same DE more than once
201
+ even if both instances are separated by a different element,
202
+ like DE 3055 and 1131 in C088 of segment FII, which you may
203
+ find in invoices. In that case the same concept holds: cde.a1131
204
+ would fetch all instances, no matter if some other elements
205
+ occur in between or not.
206
+
207
+
208
+ == Building it all together
209
+
210
+ OK, so we keep generating segments, filling their data elements
211
+ with content, and adding them to the message that we are
212
+ about to build - fine.
213
+
214
+ Likewise, we add messages to the interchange. Fine - but how do we
215
+ eventually get the output, how do we make sure that we have
216
+ not forgotten anything, and how do we deal with the
217
+ service segments (e.g. to define sender and recipient IDs) ?
218
+
219
+ === Headers and trailers
220
+
221
+ Interchanges and messages are objects which may come with
222
+ a header and trailer segment. In UN/EDIFACT, these are
223
+ UNB/UNZ and UNH/UNT, respectively.
224
+
225
+ In order to let us focus on the content, edi4r keeps these
226
+ service segments away from us and tries its best to
227
+ treat them automatically. For example, we do not have to
228
+ count segments or messages - edi4r takes care of that
229
+ and updates the corresponding DE in UNT and UNZ, respectively.
230
+
231
+ If we really need to access data there,
232
+ that's possible anytime through getters <tt>header</tt> and
233
+ <tt>trailer</tt>. From then on, we just use the usual DE and
234
+ CDE getters and setters. E.g., setting the UNB sender ID
235
+ works like this:
236
+
237
+ ic.header.cS002.d0004 = '1234567'
238
+
239
+ Setting the test indicator may look like this:
240
+
241
+ ic.header.d0035 = 1
242
+
243
+ === UNA handling
244
+
245
+ The pseudo segment UNA commonly introduces an UN/EDIFACT
246
+ interchange. It is shown there by default, which is a good
247
+ idea in most cases. If you really have to switch it off, use:
248
+
249
+ ic.show_una = false
250
+
251
+ It can be easily viewed e.g. by:
252
+
253
+ puts ic.una
254
+
255
+ The six special characters it containes are both readable
256
+ and modifiable. Please note that these characters are represented
257
+ as their ASCII integer codes, not as one-character strings.
258
+ Here is the list of corresponding getter methods:
259
+
260
+ ce_sep() # Component data element separator, default ?:
261
+ de_sep() # Data element separator, default: ?+
262
+ decimal_sign() # Both ?. and ?, are eligible
263
+ esc_char() # Escape character, default: ??
264
+ rep_sep() # Repetition occurrence indicator, default:
265
+ # ? (SV1-3), ?* (syntax version 4)
266
+ seg_term() # Segment terminator, default: ?'
267
+
268
+ Corresponding setters allow you to change all of them.
269
+ Remember to pass ASCII values, not strings. Example:
270
+
271
+ pri.cC509.d5118 = 30.1
272
+ pri.to_s --> "PRI+AAA:30.1::LIU"
273
+ ic.una.decimal_sign = ?,
274
+ pri.to_s --> "PRI+AAA:30,1::LIU"
275
+ ic.una.ce_sep = ?/
276
+ pri.to_s --> "PRI+AAA/30,1//LIU"
277
+
278
+ === Validation
279
+
280
+ Edi4r comes with a set of built-in validation rules. In order to
281
+ validate your interchange, just call
282
+
283
+ ic.validate
284
+
285
+ This method will return the number of (recoverable) issues found.
286
+ Error messages and warnings are written to $stderr. There are
287
+ a few non-recoverable errors which raise exceptions.
288
+
289
+ Actually, you may apply method validate() to almost all of the
290
+ EDI objects mentioned so far. However, doing this once for the
291
+ whole interchance will validate everything.
292
+
293
+ === Printing and saving
294
+
295
+ Once our data is validated, we want to send it to our business partner.
296
+ Actually, that's very simple: Output it e.g. to stdout or to a file
297
+ by just printing it! This works nicely, because our EDI objects are
298
+ all equipped with reasonable methods "to_s()".
299
+
300
+
301
+ == Processing (inbound) interchanges
302
+
303
+ === Building the interchange object
304
+
305
+ We build a whole interchange with a single call by passing
306
+ its character representation as a String or IO object to class method
307
+ parse():
308
+
309
+ ic = nil
310
+ File.open("inbound_01.edi") {|hnd| ic = EDI::E::Interchange.parse( hnd )}
311
+
312
+ That's it - from now on we may access all its contents in much the
313
+ same way as we did during generation.
314
+
315
+ === Iterating over messages
316
+
317
+ Typically we'd like to loop through the messages contained:
318
+
319
+ ic.each { |msg| map_message( msg ) }
320
+
321
+ with some suitably created mapping procedure map_message().
322
+ In this context, the interchange is treated as a container
323
+ of messages. We therefore use the standard iterator each().
324
+
325
+ Actually, index access is also available, as are following
326
+ array-like methods:
327
+ [](int), index(obj), each(&b), find_all(&b), size(),
328
+ length(), first(), last().
329
+
330
+ Examples:
331
+ second_msg = ic[1]
332
+ last_msg = ic.last
333
+
334
+
335
+ === Awaiting segments of a message
336
+
337
+ Similarly, we iterate through the segments of a message. The following
338
+ construction lets you select segments in their proper context
339
+ (which is the segment group):
340
+
341
+ def map_message( msg )
342
+ # do your initialization here, then
343
+
344
+ msg.each do |seg|
345
+ seg_name = seg.name
346
+ seg_name += ' ' + seg.sg_name if seg.sg_name
347
+ case seg_name
348
+ when "BGM"
349
+ # do this ...
350
+ when "DTM"
351
+ # do that ...
352
+ when 'NAD SG2'
353
+ # react only if NAD occurs in segment group 2
354
+
355
+ # ... etc., finally:
356
+ default
357
+ raise "Segment #{seg_name}: Not accounted for!"
358
+ end
359
+ end
360
+
361
+ If you need to obtain all segments with a given tag, pass the
362
+ tag as a string to the index operator:
363
+
364
+ d = msg['DTM'] # Array of all DTM segments, any segment group
365
+
366
+ Actually, a message behaves like a container just as an interchange does,
367
+ so the array-like methods listed in the previous section also apply here:
368
+
369
+ d = msg.find_all {|seg| seg.name == 'DTM' && seg.sg_name == 'SG20'}
370
+
371
+ === Selecting segment group instances
372
+
373
+ Iterating over all segments of a message sequentially tends to produce
374
+ cluttered code when messages grow complex. Wouldn't it be nice
375
+ if we could delegate e.g. the mapping of a whole NAD group to a
376
+ specialized procedure? Still better - could we delegate mapping
377
+ of a whole item group to a specialized routine?
378
+
379
+ Actually that's quite easy when we recall that segment groups
380
+ are side branches of the main trunk (or a higher-level branch)
381
+ of a message diagram, with their trigger segments being the
382
+ T-shaped "joints". Thus, segments of a segment group are mere
383
+ descendants of their trigger segment in the branching diagram.
384
+
385
+ Here are some helpful methods to select segments of a group:
386
+ # ...
387
+ when 'NAD SG2'
388
+ map_nad_sg2( seg.children_and_self ) # skip segment COM...
389
+ # ...
390
+ when 'LIN SG28'
391
+ map_item( seg.descendants_and_self )
392
+ # ...
393
+
394
+ Methods
395
+ descendants(), descendants_and_self(), children(), and
396
+ children_and_self()
397
+ are inspired by XPath axes. They return an array of segments
398
+ which depend on the given (trigger) segment as their common
399
+ ancestor.
400
+
401
+ Using these selectors, writing modular mapping code
402
+ now becomes an easy task.
403
+
404
+
405
+ == Peeking into interchanges
406
+
407
+ === Background
408
+
409
+ Sometimes we only need to extract some header information
410
+ out of EDI files, e.g. in order to find out whether the content
411
+ is UN/EDIFACT, who sent it to whom, or just to see if the
412
+ UNB test indicator is set.
413
+
414
+ We could of course apply EDI::E::Interchange.parse() and access
415
+ the header of the resulting interchange object when we know that
416
+ a given file contains UN/EDIFACT data. However, that would be
417
+ a big waste of resources, especially for large interchanges.
418
+
419
+ === Method "peek()" for UN/EDIFACT data
420
+
421
+ Edi4r instead offers method "peek()". It reads just enought bytes
422
+ from the file to determine its contents and to decode the header.
423
+ Like "parse()" it returns an Interchange object, but that one
424
+ is empty except for the header (and a dummy trailer) segment.
425
+
426
+ You can then extract any header element you need through the
427
+ usual getters. Example: Find out if the test indicator is set.
428
+
429
+ def is_testdata?( hnd )
430
+ ic = EDI::E::Interchange.peek( hnd )
431
+ ic.d0035 == '1' || ic.d0035 == 1
432
+ end
433
+
434
+ === Auto-detection and implicit decompression
435
+
436
+ Regular EDI users need to archive their business data.
437
+ In simple cases, moving interchange files into proper folders
438
+ after successfully processing them already does the job.
439
+
440
+ You can save a lot of space though by compressing them.
441
+ Applying "gzip" to EDIFACT data easily shrinks them to
442
+ 10 % of their original volume.
443
+
444
+ So far, so good. Later though, you may need to extract
445
+ a specific file from the archive, e.g. the interchange
446
+ with control reference "ref3456" from a customer
447
+ with sender id "xyz". Well, you do not need to maintain
448
+ a separate index or decompress all files in the archive
449
+ in order to find it. There is a generic class method
450
+ "Interchange.peek()" that does all this for you.
451
+ Consider the following code fragment that assumes
452
+ that ARGV contains the list of files to search:
453
+
454
+ require 'zlib'
455
+
456
+ found = ARGV.find do |fname|
457
+ ic = EDI::Interchange.peek(File.open(fname))
458
+ h = ic.header
459
+ ic.syntax=='E' && h.cS002.d0004=='xyz' && h.d0020=='ref1234'
460
+ end
461
+ ic = EDI::Interchange.parse(File.open(found))
462
+ # ...
463
+
464
+ Note that this code will work with both zipped and
465
+ unzipped data, and with UN/EDIFACT as well as other content.
466
+
467
+
468
+ == XML representation
469
+
470
+ === Background
471
+
472
+ EDI interchanges may be regarded as abstract objects which need
473
+ some representation when stored or exchanged. E.g., UN/EDIFACT
474
+ interchanges may be expressed (represented) by syntax version 1-4
475
+ and a choice of separator and termination characters
476
+ without changing their identity. Likewise, interchanges
477
+ may be represented by some suitable XML document type.
478
+
479
+ There was a time when classical EDI representations were
480
+ considered outdated and to be replaced by XML documents.
481
+ We know by now that a mere change in representation does not help
482
+ to resolve the real issues of e-business. Nonetheless,
483
+ XML-based technology has become much more wide-spread than EDI,
484
+ so chances are high that one has to integrate classical EDI data
485
+ into a XML-driven architecture.
486
+
487
+ There are (too) many ways to do this, and attempts have been made
488
+ to standardize them. In particular, DIN 16557-4
489
+ (http://www.beuth.de/cmd?workflowname=CSVList&websource=&artid=43768898)
490
+ describes a way how to represent UN/EDIFACT interchanges as XML documents
491
+ and how to describe them with DTDs.
492
+
493
+ The DIN approach however focuses only on UN/EDIFACT and does not represent
494
+ the logical structure of documents. It merely encodes segments as
495
+ XML elements and data elements and composites as their attributes.
496
+ The value of DTDs is limited, as they need to be generated for
497
+ any particular interchange and lack information about mandatory
498
+ data elements and CDEs, let alone code lists.
499
+
500
+ This library therefore supports a generic approach that allows us
501
+ to represent any interchange object as an XML document, be it
502
+ a UN/EDIFACT interchange, a file of SAP IDocs, or an ANSI
503
+ X.12 or other interchange when such a module becomes available.
504
+ Native and XML representation are fully interchangeable,
505
+ and the XML representation reflects information from the
506
+ branching diagram, thus supporting considerably XPath-based
507
+ processing (e.g. you could easily select an instance of
508
+ a line item group). Formal validation through a single generic DTD
509
+ is available, while in-depth validation remains available
510
+ through this library at the abstract level.
511
+
512
+ === Generating an XML representation of an interchange
513
+
514
+ The current implementation of XML features is built on
515
+ Ruby's REXML module (alternative implementations are conceivable).
516
+ Simply load the additional methods <em>after</em> loading all other
517
+ optional EDI4R modules, then use method "to_xml()" like this:
518
+
519
+ # Other require statements, finally:
520
+ require "edi4r/rexml"
521
+
522
+ # Generate your interchange "ic", then:
523
+ xdoc = REXML::Document.new # Empty REXML document
524
+ ic.to_xml( xdoc ) # Fill it
525
+
526
+ # The rest is standard REXML handling. Here, we write the xdoc to a file.
527
+ # (See REXML::Document.write() for details on indenting)
528
+ xdoc.write( File.open( 'mydata.xml','w'), 0 )
529
+
530
+ === Building an interchange from its XML representation
531
+
532
+ No matter what EDI standard the interchange represents,
533
+ its corresponding EDI4R object can be re-generated easily.
534
+ Just make sure that you have loaded the corresponding module(s):
535
+
536
+ # Other require statements, finally:
537
+ require "edi4r/rexml"
538
+
539
+ ic = EDI::Interchange.parse( File.open('mydata.xml') )
540
+
541
+ Yes, that's right: It's the same statement that would
542
+ also load UN/EDIFACT data!
543
+
544
+ If you know already what to expect, you might bypass EDI4R's
545
+ auto-detection and directly call one of the parse_xml() methods:
546
+
547
+ xdoc = REXML::Document.new( File.open('mydata.xml') )
548
+ ic = EDI::E::Interchange.parse_xml( xdoc )
549
+
550
+ === Utilities: edi2xml.rb, xml2edi.rb
551
+
552
+ These two scripts are included to further simplify your transition
553
+ between traditional EDI representation and XML representation.
554
+ They are command-line tools that simply wrap the library calls
555
+ mentioned above. Example:
556
+
557
+ $ edi2xml.rb foo.edi > foo.xml
558
+ $ xml2edi.rb foo.xml > bar.edi
559
+ $ diff foo.edi bar.edi # There should be no differences
560
+
561
+ == Tools
562
+
563
+ === editool.rb
564
+
565
+ Use this command-line tool e.g. when you want to
566
+ * <b>list</b> UN/EDIFACT data in a readable way (one segment per line,
567
+ optionally indented according to segment level),
568
+ * <b>validate</b> your EDI data
569
+ * <b>analyze</b> the data more thoroughly through method "inspect()"
570
+ * <b>report</b> header data quickly, one line per file
571
+
572
+ Called without option, it just builds an internal memory model
573
+ of the passed file(s) and raises an exception upon parsing errors.
574
+ Thorough validation can be requested optionally. Example:
575
+
576
+ $ editool.rb -l foo.edi bar.edi
577
+ $ editool.rb -p *.edi *.xml
578
+
579
+
580
+ == Further reading
581
+
582
+ A pair of full-blown mappings (inhouse-to-EANCOM, EANCOM-to-inhouse)
583
+ shows in much more detail how to do outbound and inbound mapping.
584
+ See the source codes in the "test" folder!
585
+
586
+
587
+ == Misc topics
588
+
589
+ To be supplied later; currently just a room to collect keywords to cover.
590
+
591
+ === Debugging and viewing
592
+
593
+ :linebreak, :indented
594
+ inspect()
595
+ Exception classes
596
+ Segments: T-nodes, ordinal number, index, occurrence, max. occurrence
597
+ empty?, required?
598
+
599
+ === More advances features
600
+
601
+ Validation: warnings and exceptions (logging?)
602
+ Add-ons to class Time
603
+ Consistency of references common to headers and trailers
604
+ Inheriting settings from parent: UNH from UNG, UNG from UNB
605
+ Low-level access to Collection contents
606
+
607
+ Enjoy,
608
+
609
+ -- Heinz