edifunct 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/README.md +188 -0
- data/README_generate.rb +102 -0
- data/Rakefile +8 -0
- data/bin/console +7 -0
- data/bin/setup +6 -0
- data/edifunct.gemspec +29 -0
- data/lib/edifunct/parser.rb +53 -0
- data/lib/edifunct/segment.rb +14 -0
- data/lib/edifunct/segment_group.rb +73 -0
- data/lib/edifunct/tokenizer.rb +71 -0
- data/lib/edifunct/version.rb +5 -0
- data/lib/edifunct.rb +25 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bae32c4270171e87b5995a301dbb00c2bbbd494d74796c5b08399771f50c5f04
|
4
|
+
data.tar.gz: 0aabf021fc6a986ad70fe2cf37c5804fd3b53681452e311d9885385d77c7c792
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 309d7c2bffda2a3950ea61262ab17acae1d1fc355cfae735adecd62389dd94ea50171f8920d3171251e2e5ece8c08e250cf2fc51b52eee77ed574ce641bd370c
|
7
|
+
data.tar.gz: 2ca8e55858831214be0972e9b04b56c7f269eb80ae27a6e43c3eb6ad56a34d75584ad483c0925117328d3f44177443610ce78df2d67a1c289c5eefe6b6f68121
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
# Edifunct
|
2
|
+
|
3
|
+
Fun with EDIFACT :tada:
|
4
|
+
|
5
|
+
EDIFACT files consist of segments and extracting the segments themselves is not too complex.
|
6
|
+
But when segments are being grouped in segment groups and nested segment groups, it would require having an additional, manual parsing step after extracting the segments.
|
7
|
+
This gem makes this easy by parsing the EDIFACT file according to a simple schema which you provide alongside the EDIFACT file.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'edifunct'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install edifunct
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
An example EDIFACT file:
|
28
|
+
|
29
|
+
```
|
30
|
+
UNB+UNOC:4+5790000110018:14+SEAFT.AFT006+20151012:1354+31'
|
31
|
+
UNH+45689+IFTSTA:D:10B:UN'
|
32
|
+
BGM+77+YSTSE-39237+9'
|
33
|
+
DTM+137:20150820:203'
|
34
|
+
DTM+2:20150820:102'
|
35
|
+
DTM+234:20150820:102'
|
36
|
+
CNI+1+DSVS41599'
|
37
|
+
CNT+7:9:KGM'
|
38
|
+
STS++Z1+5+8+38+108'
|
39
|
+
RFF+BN:123456'
|
40
|
+
RFF+ZTF:9F'
|
41
|
+
RFF+CU:8008331140'
|
42
|
+
RFF+AAS:7481947187'
|
43
|
+
RFF+SRN:57065930000021747'
|
44
|
+
RFF+AAM:40157065930100420983'
|
45
|
+
RFF+ASI:FM807287'
|
46
|
+
DTM+334:201508201020:203'
|
47
|
+
FTX+AHN+++Collect remarks1:2:3:4:5'
|
48
|
+
NAD+AP'
|
49
|
+
CTA+GR+:DONALD DRIVER'
|
50
|
+
NAD+CZ+++UAE LOGISTICS AB+BOX 1001:SE-164 21 KISTA+KISTA++164 21+SE'
|
51
|
+
NAD+CN+++SIERSEMA+KEURWEG 2:.:NL 5145 NX 5145 NX WAALWIJK+5145 NX WAALWIJK++5145 NX+NL'
|
52
|
+
NAD+DP+++BYGMA KOLDING+GEJLHAVEG�RD 2 A:DK-6000 KOLDING+KOLDING++6000+DK'
|
53
|
+
NAD+PW+++NAUTISK TEKNIK APS+FARUMVEJ 107, GANL�SE:V/MARTIN KRISTENSEN:DK-3660 STENL�SE+STENL�SE++3660+DK'
|
54
|
+
NAD+ST+123456++ALFA LAVAL KOLDING A/S+31 ALBUEN:DK-6000 KOLDING+KOLDING++6000+DK'
|
55
|
+
NAD+SF+789456++SANISTAL SIA+Tiraines iela 9+Riga++1058+LV'
|
56
|
+
LOC+Z01+SELAA::6:SELAA LANDSKRONA'
|
57
|
+
GID+1+1:PK'
|
58
|
+
LOC+14+LANDSKRONA'
|
59
|
+
MEA+WT+AAB+KGM:100'
|
60
|
+
MEA+WT+ADZ+KGM:90'
|
61
|
+
MEA+VOL++MTQ:2'
|
62
|
+
MEA+LMT++MTR:4'
|
63
|
+
MEA+CT+SQ+PLL:5'
|
64
|
+
DIM+1+MTR:0.2:0.1:0.1'
|
65
|
+
PCI+17'
|
66
|
+
GIN+BN+00073000093496312546:00073000090414361624+00073000090414361631:00073000090414361648+00073000090414361655:00073000093496312539+00073000123496312546:00073000053496312546+00073000093496312789:00073000093496312684'
|
67
|
+
PCI+18'
|
68
|
+
GIN+AW+373999991234567899:373323995756893927+373323995780867383:373323995756893927+373323995859384889:373323995859360043+373323995859387804:373323995859387811+373323995859387842:373323995859392068'
|
69
|
+
UNT+18+45689'
|
70
|
+
UNZ+1+31'
|
71
|
+
```
|
72
|
+
|
73
|
+
A schema can be described entirely in JSON. An example schema for the EDIFACT file above could look like this:
|
74
|
+
|
75
|
+
```json
|
76
|
+
[
|
77
|
+
{ "type": "segment", "segment_tag": "UNB" },
|
78
|
+
{ "type": "segment", "segment_tag": "UNH" },
|
79
|
+
{ "type": "segment", "segment_tag": "BGM" },
|
80
|
+
{ "type": "segment", "segment_tag": "DTM", "repeat": true },
|
81
|
+
{
|
82
|
+
"type": "segment_group",
|
83
|
+
"group_name": "SG13",
|
84
|
+
"content": [
|
85
|
+
{ "type": "segment", "segment_tag": "CNI" },
|
86
|
+
{ "type": "segment", "segment_tag": "CNT", "repeat": true },
|
87
|
+
{
|
88
|
+
"type": "segment_group",
|
89
|
+
"group_name": "SG14",
|
90
|
+
"content": [
|
91
|
+
{ "type": "segment", "segment_tag": "STS" },
|
92
|
+
{ "type": "segment", "segment_tag": "RFF", "repeat": true },
|
93
|
+
{ "type": "segment", "segment_tag": "DTM", "repeat": true },
|
94
|
+
{ "type": "segment", "segment_tag": "FTX", "repeat": true },
|
95
|
+
{
|
96
|
+
"type": "segment_group",
|
97
|
+
"group_name": "SG15",
|
98
|
+
"content": [
|
99
|
+
{ "type": "segment", "segment_tag": "NAD" },
|
100
|
+
{
|
101
|
+
"type": "segment_group",
|
102
|
+
"group_name": "SG16",
|
103
|
+
"content": [
|
104
|
+
{ "type": "segment", "segment_tag": "CTA" }
|
105
|
+
]
|
106
|
+
}
|
107
|
+
]
|
108
|
+
},
|
109
|
+
{ "type": "segment", "segment_tag": "LOC" },
|
110
|
+
{
|
111
|
+
"type": "segment_group",
|
112
|
+
"group_name": "SG23",
|
113
|
+
"content": [
|
114
|
+
{ "type": "segment", "segment_tag": "GID" },
|
115
|
+
{ "type": "segment", "segment_tag": "LOC", "repeat": true },
|
116
|
+
{
|
117
|
+
"type": "segment_group",
|
118
|
+
"group_name": "SG24",
|
119
|
+
"content": [
|
120
|
+
{ "type": "segment", "segment_tag": "MEA" }
|
121
|
+
]
|
122
|
+
},
|
123
|
+
{
|
124
|
+
"type": "segment_group",
|
125
|
+
"group_name": "SG25",
|
126
|
+
"content": [
|
127
|
+
{ "type": "segment", "segment_tag": "DIM" }
|
128
|
+
]
|
129
|
+
},
|
130
|
+
{
|
131
|
+
"type": "segment_group",
|
132
|
+
"group_name": "SG26",
|
133
|
+
"content": [
|
134
|
+
{ "type": "segment", "segment_tag": "PCI" },
|
135
|
+
{ "type": "segment", "segment_tag": "GIN", "repeat": true }
|
136
|
+
]
|
137
|
+
}
|
138
|
+
]
|
139
|
+
}
|
140
|
+
]
|
141
|
+
}
|
142
|
+
]
|
143
|
+
},
|
144
|
+
{ "type": "segment", "segment_tag": "UNT" },
|
145
|
+
{ "type": "segment", "segment_tag": "UNZ" }
|
146
|
+
]
|
147
|
+
```
|
148
|
+
|
149
|
+
Now we are ready to parse the EDIFACT file:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
# iftsta_example_as_string: EDIFACT file (String)
|
153
|
+
# iftsta_schema: Schema (Array<Hash>)
|
154
|
+
|
155
|
+
iftsta_example = Edifunct.parse(iftsta_example_as_string, schema: iftsta_schema)
|
156
|
+
iftsta_example.lookup_groups('SG13').each_with_object({}) do |group, consignments|
|
157
|
+
sg14 = group.lookup_group('SG14')
|
158
|
+
next unless sg14
|
159
|
+
|
160
|
+
rff_cu_record = sg14.lookup_segment('RFF') { |s| s.data_elements[0] && s.data_elements[0][0] == 'CU' }
|
161
|
+
next unless rff_cu_record
|
162
|
+
|
163
|
+
dtm_record = sg14.lookup_segment('DTM')
|
164
|
+
next unless dtm_record
|
165
|
+
|
166
|
+
sts_record = sg14.lookup_segment('STS')
|
167
|
+
next unless sts_record
|
168
|
+
|
169
|
+
consignment_ref = rff_cu_record.data_elements[0][1]
|
170
|
+
consignments[consignment_ref] = [
|
171
|
+
{
|
172
|
+
status: sts_record.data_elements[1][0],
|
173
|
+
event_time: dtm_record.data_elements[0][1],
|
174
|
+
}
|
175
|
+
]
|
176
|
+
end
|
177
|
+
# => {"8008331140"=>[{:status=>"Z1", :event_time=>"201508201020"}]}
|
178
|
+
```
|
179
|
+
|
180
|
+
## Development
|
181
|
+
|
182
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
183
|
+
|
184
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
185
|
+
|
186
|
+
## Contributing
|
187
|
+
|
188
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/orhantoy/edifunct.
|
data/README_generate.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "json"
|
3
|
+
require "edifunct"
|
4
|
+
|
5
|
+
puts "# Edifunct"
|
6
|
+
puts
|
7
|
+
puts <<-MARKDOWN
|
8
|
+
Fun with EDIFACT :tada:
|
9
|
+
|
10
|
+
EDIFACT files consist of segments and extracting the segments themselves is not too complex.
|
11
|
+
But when segments are being grouped in segment groups and nested segment groups, it would require having an additional, manual parsing step after extracting the segments.
|
12
|
+
This gem makes this easy by parsing the EDIFACT file according to a simple schema which you provide alongside the EDIFACT file.
|
13
|
+
MARKDOWN
|
14
|
+
|
15
|
+
puts "\n## Installation"
|
16
|
+
puts
|
17
|
+
puts <<-MARKDOWN
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'edifunct'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install edifunct
|
31
|
+
MARKDOWN
|
32
|
+
|
33
|
+
fixtures_dir = File.join(__dir__, "spec", "fixtures")
|
34
|
+
iftsta_example_as_string = File.read(File.join(fixtures_dir, "IFTSTA_example.edi"), encoding: "ISO-8859-1")
|
35
|
+
iftsta_schema_json = File.read(File.join(fixtures_dir, "IFTSTA_schema.json"))
|
36
|
+
iftsta_schema = JSON.parse(iftsta_schema_json)
|
37
|
+
|
38
|
+
puts "\n## Usage"
|
39
|
+
puts
|
40
|
+
puts "An example EDIFACT file:"
|
41
|
+
puts
|
42
|
+
puts "```"
|
43
|
+
print iftsta_example_as_string
|
44
|
+
puts "```"
|
45
|
+
puts
|
46
|
+
puts "A schema can be described entirely in JSON. An example schema for the EDIFACT file above could look like this:"
|
47
|
+
puts
|
48
|
+
puts "```json"
|
49
|
+
print iftsta_schema_json
|
50
|
+
puts "```"
|
51
|
+
puts
|
52
|
+
puts "Now we are ready to parse the EDIFACT file:"
|
53
|
+
puts
|
54
|
+
puts "```ruby"
|
55
|
+
puts "# iftsta_example_as_string: EDIFACT file (String)"
|
56
|
+
puts "# iftsta_schema: Schema (Array<Hash>)"
|
57
|
+
puts
|
58
|
+
|
59
|
+
prog = <<-PROG
|
60
|
+
iftsta_example = Edifunct.parse(iftsta_example_as_string, schema: iftsta_schema)
|
61
|
+
iftsta_example.lookup_groups('SG13').each_with_object({}) do |group, consignments|
|
62
|
+
sg14 = group.lookup_group('SG14')
|
63
|
+
next unless sg14
|
64
|
+
|
65
|
+
rff_cu_record = sg14.lookup_segment('RFF') { |s| s.data_elements[0] && s.data_elements[0][0] == 'CU' }
|
66
|
+
next unless rff_cu_record
|
67
|
+
|
68
|
+
dtm_record = sg14.lookup_segment('DTM')
|
69
|
+
next unless dtm_record
|
70
|
+
|
71
|
+
sts_record = sg14.lookup_segment('STS')
|
72
|
+
next unless sts_record
|
73
|
+
|
74
|
+
consignment_ref = rff_cu_record.data_elements[0][1]
|
75
|
+
consignments[consignment_ref] = [
|
76
|
+
{
|
77
|
+
status: sts_record.data_elements[1][0],
|
78
|
+
event_time: dtm_record.data_elements[0][1],
|
79
|
+
}
|
80
|
+
]
|
81
|
+
end
|
82
|
+
PROG
|
83
|
+
|
84
|
+
print prog
|
85
|
+
consignments = eval prog
|
86
|
+
puts "# => #{consignments.inspect}"
|
87
|
+
|
88
|
+
puts "```"
|
89
|
+
|
90
|
+
puts "\n## Development"
|
91
|
+
puts
|
92
|
+
puts <<-MARKDOWN
|
93
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
94
|
+
|
95
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
96
|
+
MARKDOWN
|
97
|
+
|
98
|
+
puts "\n## Contributing"
|
99
|
+
puts
|
100
|
+
puts <<-MARKDOWN
|
101
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/orhantoy/edifunct.
|
102
|
+
MARKDOWN
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
data/edifunct.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "edifunct/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "edifunct"
|
9
|
+
spec.version = Edifunct::VERSION
|
10
|
+
spec.authors = ["Orhan Toy"]
|
11
|
+
spec.email = ["toyorhan@gmail.com"]
|
12
|
+
|
13
|
+
spec.summary = 'A schema-based EDIFACT parser'
|
14
|
+
spec.description = 'Edifunct provides an easy way to structurally parse an EDIFACT file based on a simple schema'
|
15
|
+
spec.homepage = 'https://github.com/orhantoy/edifunct'
|
16
|
+
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "edifunct/tokenizer"
|
4
|
+
require "edifunct/segment_group"
|
5
|
+
|
6
|
+
module Edifunct
|
7
|
+
# The main entry point to converting a raw EDIFACT message into a structured object.
|
8
|
+
# The structure is dictated by the provided schema.
|
9
|
+
class Parser
|
10
|
+
def initialize(message, schema:, tokenizer: nil)
|
11
|
+
@message = message
|
12
|
+
@schema = schema
|
13
|
+
@tokenizer = tokenizer || Tokenizer.for_message(message)
|
14
|
+
end
|
15
|
+
|
16
|
+
def as_root_group(root_group_tag = "<root>")
|
17
|
+
@segments = @tokenizer.as_segments(@message)
|
18
|
+
|
19
|
+
SegmentGroup.new(root_group_tag).tap do |root_group|
|
20
|
+
parse_group(root_group, schema)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :schema, :segments
|
27
|
+
|
28
|
+
def parse_group(parent_group, group_schema)
|
29
|
+
group_schema.each do |schema_entry|
|
30
|
+
case schema_entry.fetch("type")
|
31
|
+
when "segment"
|
32
|
+
if current_segment_is?(schema_entry.fetch("segment_tag"))
|
33
|
+
parent_group.add_segment(segments.shift)
|
34
|
+
parent_group.add_segment(segments.shift) while schema_entry["repeat"] && current_segment_is?(schema_entry.fetch("segment_tag"))
|
35
|
+
end
|
36
|
+
when "segment_group"
|
37
|
+
while current_segment_is?(schema_entry.fetch("content").fetch(0).fetch("segment_tag"))
|
38
|
+
group = parent_group.create_group(schema_entry.fetch("group_name"))
|
39
|
+
parse_group(group, schema_entry.fetch("content"))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def current_segment_is?(tag)
|
46
|
+
current_segment && current_segment.tag == tag
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_segment
|
50
|
+
segments[0]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Edifunct
|
4
|
+
# Represents a segment in an EDIFACT document/message.
|
5
|
+
class Segment
|
6
|
+
attr_reader :tag, :raw_segment, :data_elements
|
7
|
+
|
8
|
+
def initialize(tag:, raw_segment:, data_elements:)
|
9
|
+
@tag = tag
|
10
|
+
@raw_segment = raw_segment
|
11
|
+
@data_elements = data_elements
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "edifunct/segment"
|
4
|
+
|
5
|
+
module Edifunct
|
6
|
+
# Represents the logical grouping of segments.
|
7
|
+
class SegmentGroup
|
8
|
+
attr_reader :tag
|
9
|
+
attr_reader :children
|
10
|
+
|
11
|
+
def initialize(tag)
|
12
|
+
@tag = tag
|
13
|
+
@children = []
|
14
|
+
|
15
|
+
@child_segment_map = Hash.new { |hash, key| hash[key] = [] }
|
16
|
+
@child_segment_group_map = Hash.new { |hash, key| hash[key] = [] }
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_segment(segment)
|
20
|
+
@children << segment
|
21
|
+
@child_segment_map[segment.tag] << segment
|
22
|
+
|
23
|
+
segment
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_group(segment_group_tag)
|
27
|
+
segment_group = SegmentGroup.new(segment_group_tag)
|
28
|
+
|
29
|
+
@children << segment_group
|
30
|
+
@child_segment_group_map[segment_group.tag] << segment_group
|
31
|
+
|
32
|
+
segment_group
|
33
|
+
end
|
34
|
+
|
35
|
+
def lookup_groups(segment_group_tag)
|
36
|
+
@child_segment_group_map[segment_group_tag]
|
37
|
+
end
|
38
|
+
|
39
|
+
def lookup_group(segment_group_tag)
|
40
|
+
lookup_groups(segment_group_tag).first
|
41
|
+
end
|
42
|
+
|
43
|
+
def lookup_segments(segment_tag)
|
44
|
+
@child_segment_map[segment_tag]
|
45
|
+
end
|
46
|
+
|
47
|
+
def lookup_segment(segment_tag, &block)
|
48
|
+
if block_given?
|
49
|
+
lookup_segments(segment_tag).find(&block)
|
50
|
+
else
|
51
|
+
lookup_segments(segment_tag).first
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def print_with_structure
|
56
|
+
_print_with_structure(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def _print_with_structure(group, padding: "")
|
62
|
+
group.children.each do |child|
|
63
|
+
case child
|
64
|
+
when Segment
|
65
|
+
print "#{padding}#{child.raw_segment}\n"
|
66
|
+
when SegmentGroup
|
67
|
+
print "#{padding}#{child.tag}\n"
|
68
|
+
_print_with_structure(child, padding: padding + " ")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "edifunct/segment"
|
4
|
+
|
5
|
+
module Edifunct
|
6
|
+
# Tokenizer is responsible for splitting message into segments, data elements and components.
|
7
|
+
class Tokenizer
|
8
|
+
class << self
|
9
|
+
def for_message(_edifact_message)
|
10
|
+
# TODO: Should check if the message starts with `UNA`, and then extract the different separator/terminator settings to be used for initializing the tokenizer.
|
11
|
+
new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :release_character, :segment_terminator, :data_element_separator, :component_data_element_separator
|
16
|
+
|
17
|
+
def initialize(release_character: "?", segment_terminator: "'", data_element_separator: "+", component_data_element_separator: ":")
|
18
|
+
@release_character = release_character
|
19
|
+
@segment_terminator = segment_terminator
|
20
|
+
@data_element_separator = data_element_separator
|
21
|
+
@component_data_element_separator = component_data_element_separator
|
22
|
+
end
|
23
|
+
|
24
|
+
def as_segments(message_as_string)
|
25
|
+
message_as_string.split(segment_regexp).map do |raw_segment|
|
26
|
+
segment_tag, data_elements = split_segment(raw_segment)
|
27
|
+
|
28
|
+
Segment.new(tag: segment_tag, raw_segment: raw_segment, data_elements: data_elements)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def split_segment(raw_segment)
|
33
|
+
segment_without_terminator = raw_segment.chomp(@segment_terminator)
|
34
|
+
segment_tag, *data_elements_as_strings = segment_without_terminator.split(data_element_regexp)
|
35
|
+
|
36
|
+
data_elements = data_elements_as_strings.map do |data_element_as_string|
|
37
|
+
data_element_as_string.split(component_data_element_regexp).map do |component|
|
38
|
+
decode_value(component)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
[segment_tag, data_elements]
|
43
|
+
end
|
44
|
+
|
45
|
+
def decode_value(encoded_value)
|
46
|
+
encoded_value.gsub(escape_value_regexp, '\1')
|
47
|
+
end
|
48
|
+
|
49
|
+
def formatted_segments_per_line(message_as_string)
|
50
|
+
message_as_string.gsub(segment_regexp, "\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def escape_value_regexp
|
56
|
+
@escape_value_regexp ||= Regexp.new("#{Regexp.escape(@release_character)}(#{Regexp.escape(@release_character)}|#{Regexp.escape(@segment_terminator)}|#{Regexp.escape(@data_element_separator)}|#{Regexp.escape(@component_data_element_separator)})")
|
57
|
+
end
|
58
|
+
|
59
|
+
def segment_regexp
|
60
|
+
@segment_regexp ||= Regexp.new("(?!#{Regexp.escape(@release_character)})(?<=#{Regexp.escape(@segment_terminator)})\\s*")
|
61
|
+
end
|
62
|
+
|
63
|
+
def data_element_regexp
|
64
|
+
@data_element_regexp ||= Regexp.new("(?!#{Regexp.escape(@release_character)})#{Regexp.escape(@data_element_separator)}")
|
65
|
+
end
|
66
|
+
|
67
|
+
def component_data_element_regexp
|
68
|
+
@component_data_element_regexp ||= Regexp.new("(?!#{Regexp.escape(@release_character)})#{Regexp.escape(@component_data_element_separator)}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/edifunct.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "edifunct/version"
|
4
|
+
require "edifunct/parser"
|
5
|
+
require "edifunct/tokenizer"
|
6
|
+
|
7
|
+
# Top-level Edifunct namespace with short-hands methods to parse EDIFACT documents.
|
8
|
+
module Edifunct
|
9
|
+
class << self
|
10
|
+
def parse(edifact_message, schema:)
|
11
|
+
parser = Parser.new(edifact_message, schema: schema)
|
12
|
+
parser.as_root_group
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse_file(file_args, schema:)
|
16
|
+
edifact_message = File.read(*Array(file_args))
|
17
|
+
parse(edifact_message, schema: schema)
|
18
|
+
end
|
19
|
+
|
20
|
+
def as_segments(edifact_message)
|
21
|
+
tokenizer = Tokenizer.for_message(edifact_message)
|
22
|
+
tokenizer.as_segments(edifact_message)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: edifunct
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Orhan Toy
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-01-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: Edifunct provides an easy way to structurally parse an EDIFACT file based
|
56
|
+
on a simple schema
|
57
|
+
email:
|
58
|
+
- toyorhan@gmail.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- ".rspec"
|
65
|
+
- ".travis.yml"
|
66
|
+
- Gemfile
|
67
|
+
- README.md
|
68
|
+
- README_generate.rb
|
69
|
+
- Rakefile
|
70
|
+
- bin/console
|
71
|
+
- bin/setup
|
72
|
+
- edifunct.gemspec
|
73
|
+
- lib/edifunct.rb
|
74
|
+
- lib/edifunct/parser.rb
|
75
|
+
- lib/edifunct/segment.rb
|
76
|
+
- lib/edifunct/segment_group.rb
|
77
|
+
- lib/edifunct/tokenizer.rb
|
78
|
+
- lib/edifunct/version.rb
|
79
|
+
homepage: https://github.com/orhantoy/edifunct
|
80
|
+
licenses: []
|
81
|
+
metadata: {}
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.7.6
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: A schema-based EDIFACT parser
|
102
|
+
test_files: []
|