Skip to content

Commit f962c68

Browse files
committed
Add core functionality
1 parent 8c0bb8a commit f962c68

17 files changed

+678
-33
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
/pkg/
77
/spec/reports/
88
/tmp/
9+
/Gemfile.lock
910

1011
# rspec failure tracking
1112
.rspec_status

Diff for: README.md

+157-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# Edifunct
22

3-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/edifunct`. To experiment with that code, run `bin/console` for an interactive prompt.
3+
Fun with EDIFACT :tada:
44

5-
TODO: Delete this and the text above, and describe your gem
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.
68

79
## Installation
810

@@ -22,7 +24,158 @@ Or install it yourself as:
2224

2325
## Usage
2426

25-
TODO: Write usage instructions here
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+
```
26179

27180
## Development
28181

@@ -32,4 +185,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32185

33186
## Contributing
34187

35-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/edifunct.
188+
Bug reports and pull requests are welcome on GitHub at https://github.com/orhantoy/edifunct.

Diff for: README_generate.rb

+102
Original file line numberDiff line numberDiff line change
@@ -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

Diff for: Rakefile

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require "bundler/gem_tasks"
24
require "rspec/core/rake_task"
35

Diff for: bin/console

-7
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,5 @@
33
require "bundler/setup"
44
require "edifunct"
55

6-
# You can add fixtures and/or initialization code here to make experimenting
7-
# with your gem easier. You can also use a different console, if you like.
8-
9-
# (If you use this, don't forget to add pry to your Gemfile!)
10-
# require "pry"
11-
# Pry.start
12-
136
require "irb"
147
IRB.start(__FILE__)

Diff for: bin/setup

-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,3 @@ IFS=$'\n\t'
44
set -vx
55

66
bundle install
7-
8-
# Do any other automated setup that you need to do here

Diff for: edifunct.gemspec

+4-16
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# frozen_string_literal: true
12

23
lib = File.expand_path("../lib", __FILE__)
34
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
@@ -9,22 +10,9 @@ Gem::Specification.new do |spec|
910
spec.authors = ["Orhan Toy"]
1011
spec.email = ["[email protected]"]
1112

12-
spec.summary = %q{TODO: Write a short summary, because RubyGems requires one.}
13-
spec.description = %q{TODO: Write a longer description or delete this line.}
14-
spec.homepage = "TODO: Put your gem's website or public repo URL here."
15-
16-
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17-
# to allow pushing to a single host or delete this section to allow pushing to any host.
18-
if spec.respond_to?(:metadata)
19-
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
20-
21-
spec.metadata["homepage_uri"] = spec.homepage
22-
spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
23-
spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
24-
else
25-
raise "RubyGems 2.0 or newer is required to protect against " \
26-
"public gem pushes."
27-
end
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'
2816

2917
# Specify which files should be added to the gem when it is released.
3018
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.

Diff for: lib/edifunct.rb

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
1+
# frozen_string_literal: true
2+
13
require "edifunct/version"
4+
require "edifunct/parser"
5+
require "edifunct/tokenizer"
26

7+
# Top-level Edifunct namespace with short-hands methods to parse EDIFACT documents.
38
module Edifunct
4-
class Error < StandardError; end
5-
# Your code goes here...
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
625
end

0 commit comments

Comments
 (0)