edi4r 0.9.4.1

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