atco 1.0.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 215cf54f3257e776c20d54d0177731f06cf62eda409e7763457c709757aa1a52
4
- data.tar.gz: 97ebd8cf82c779b872f23204079a230aae0d9b159bacdf3db5042cada877a197
3
+ metadata.gz: 59b747dbf14f2f4ab07ca86c8344fda6294efbf4d7b83cdd11fcfa45a9e4961b
4
+ data.tar.gz: 276916993eae21683270e35ba8d4d167820eb2ae65dd410ab535f94a7ba50134
5
5
  SHA512:
6
- metadata.gz: 69b1de96881df1044138cbebe82ea3e22053c47d36aa38dbb39c1018683675eb44e27619be1d29346d5ef156f924c3ee8e408808aec11b7940ad2ca4877dfc25
7
- data.tar.gz: d14f0b410610dbaf1d2e4e80ce41703becb6fddc99c06bbe3a88500d65bde6b3b44bdfa7768405682c654abf8cb0ecb5470a7589b7f6b978f0933db2e230736e
6
+ metadata.gz: 54cf093ae098cc2fbc2b38067ffeeec9fa7a513107b00a4cb7822b8c0b65e273b016be3465dffed59403a9f88ce9f4cfb3c750236fb8c1354ffa9347511b26ca
7
+ data.tar.gz: 92c5635e38a717f4ba9f0e036d081463b4d84bb5bd3d1788d99b191b54e8bcd09f6a44bc114f5495302a191fc47b8887f8c97934696c7a5b6f3ad9d6217fc544
data/lib/atco/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Atco
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.3"
5
5
  end
data/lib/atco.rb CHANGED
@@ -9,6 +9,8 @@ require_relative "atco/version"
9
9
 
10
10
  # Public: Atco is a module that provides a parser for the ATCO-CIF data format.
11
11
  module Atco # rubocop:disable Metrics/ModuleLength
12
+ class UnidentifiedRecordError < StandardError; end
13
+
12
14
  class << self # rubocop:disable Metrics/ClassLength
13
15
  @path = nil
14
16
  METHODS = {
@@ -21,6 +23,7 @@ module Atco # rubocop:disable Metrics/ModuleLength
21
23
  origin: "QO",
22
24
  journey_header: "QS"
23
25
  }.freeze
26
+ METHODS_BY_RECORD_IDENTITY = METHODS.invert.freeze
24
27
 
25
28
  def parse(file) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
26
29
  @path = File.expand_path(file)
@@ -32,13 +35,20 @@ module Atco # rubocop:disable Metrics/ModuleLength
32
35
  locations = []
33
36
  journeys = {}
34
37
  header = nil
38
+ unparsed = []
35
39
 
36
- data.each do |line|
40
+ data.each_with_index do |line, line_number| # rubocop:disable Metrics/BlockLength
37
41
  if line == data.first
38
42
  header = parse_header(line)
39
43
  next
40
44
  end
41
- METHODS.each do |method, identifier|
45
+
46
+ identifier = line[0, 2]
47
+ method = METHODS_BY_RECORD_IDENTITY[identifier]
48
+
49
+ begin
50
+ raise UnidentifiedRecordError, "Unidentified record: #{identifier}" unless method
51
+
42
52
  object = send("parse_#{method}", line)
43
53
  next unless object[:record_identity] && object[:record_identity] == identifier
44
54
 
@@ -59,9 +69,12 @@ module Atco # rubocop:disable Metrics/ModuleLength
59
69
  end
60
70
  end
61
71
  objects << object
72
+ rescue UnidentifiedRecordError
73
+ unparsed << {line: line, line_number: line_number}
74
+ next
62
75
  end
63
76
  end
64
- { header: header, locations: locations, journeys: journeys }
77
+ { header: header, locations: locations, journeys: journeys, unparsed: unparsed }
65
78
  end
66
79
 
67
80
  def parse_header(string)
data/spec/atco_spec.rb CHANGED
@@ -1,175 +1,217 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "#{File.dirname(__FILE__)}/spec_helper"
4
3
  require "json"
5
4
 
6
- describe Atco do # rubocop:disable Metrics/BlockLength
5
+ RSpec.describe Atco do # rubocop:disable Metrics/BlockLength
7
6
  it "has a version number" do
8
7
  expect(Atco::VERSION).not_to be nil
9
8
  end
10
9
 
11
- it "should output file for debugging!" do
12
- result = Atco.parse("spec/fixtures/example.cif")
13
- File.open("test.output", "w+") do |f|
14
- f.flush
15
- f.write(JSON.pretty_generate(result))
16
- end
17
- end
18
-
19
10
  it "should parse header from fixture" do
20
11
  result = Atco.parse("spec/fixtures/example.cif")
21
- expect result[:header] == {
22
- file_type: "ATCO-CIF",
23
- file_originator: "Electronic Registration",
24
- source_product: "MIA 4.20.18",
25
- version: "5.0",
26
- production_datetime: "20090915113809"
27
- }
12
+ expect(result[:header]).to eq(
13
+ {
14
+ file_type: "ATCO-CIF",
15
+ file_originator: "Electronic Registration",
16
+ source_product: "MIA 4.20.18",
17
+ version: "5.0",
18
+ production_datetime: "20090915113809"
19
+ }
20
+ )
28
21
  end
29
22
 
30
23
  it "should parse locations from fixture" do
31
24
  result = Atco.parse("spec/fixtures/example.cif")
32
- expect result[:header] == {
33
- file_type: "ATCO-CIF",
34
- file_originator: "Electronic Registration",
35
- source_product: "MIA 4.20.18",
36
- version: "5.0",
37
- production_datetime: "20090915113809"
38
- }
25
+ expect(result[:header]).to eq(
26
+ {
27
+ file_type: "ATCO-CIF",
28
+ file_originator: "Electronic Registration",
29
+ source_product: "MIA 4.20.18",
30
+ version: "5.0",
31
+ production_datetime: "20090915113809"
32
+ }
33
+ )
39
34
  end
40
35
 
41
36
  it "should parse header" do
42
- expect Atco.parse_header("ATCO-CIF0500Electronic Registration MIA 4.20.18 20090915113809\r\n") == {
43
- file_type: "ATCO-CIF",
44
- version: "5.0",
45
- file_originator: "Electronic Registration",
46
- source_product: "MIA 4.20.18",
47
- production_datetime: "20090915113809"
48
- }
37
+ expect(Atco.parse_header("ATCO-CIF0500Electronic Registration MIA 4.20.18 20090915113809\r\n")).to eq(
38
+ {
39
+ file_type: "ATCO-CIF",
40
+ version: "5.0",
41
+ file_originator: "Electronic Registration",
42
+ source_product: "MIA 4.20.18",
43
+ production_datetime: "20090915113809"
44
+ }
45
+ )
49
46
  end
50
47
 
51
48
  it "should parse bank holiday" do
52
- expect Atco.parse_bank_holiday("QHN20061225") == {
53
- record_identity: "QH",
54
- transaction_type: "N",
55
- date_of_bank_holiday: "20061225"
56
- }
49
+ expect(Atco.parse_bank_holiday("QHN20061225")).to eq(
50
+ {
51
+ record_identity: "QH",
52
+ transaction_type: "N",
53
+ date_of_bank_holiday: "20061225"
54
+ }
55
+ )
57
56
  end
58
57
 
59
58
  it "should parse operator" do
60
- expect Atco.parse_operator("QPNTM Translink Metro Translink Metro \r\n") == {
61
- record_identity: "QP",
62
- transaction_type: "N",
63
- operator: "TM",
64
- operator_short_form: "Translink Metro",
65
- operator_legal_name: "Translink Metro"
66
- }
59
+ expect(Atco.parse_operator("QPNTM Translink Metro Translink Metro \r\n")).to eq(
60
+ {
61
+ record_identity: "QP",
62
+ transaction_type: "N",
63
+ operator: "TM",
64
+ operator_short_form: "Translink Metro",
65
+ operator_legal_name: "Translink Metro"
66
+ }
67
+ )
67
68
  end
68
69
 
69
70
  it "should parse additional location information" do
70
- expect Atco.parse_additional_location_info("QBN700000001252 328622 367433 \r\n") == {
71
- record_identity: "QB",
72
- transaction_type: "N",
73
- location: "700000001252",
74
- grid_reference_easting: "328622",
75
- grid_reference_northing: "367433"
76
- }
71
+ expect(Atco.parse_additional_location_info("QBN700000001252 328622 367433 \r\n")).to eq(
72
+ {
73
+ record_identity: "QB",
74
+ transaction_type: "N",
75
+ location: "700000001252",
76
+ grid_reference_easting: "328622",
77
+ grid_reference_northing: "367433"
78
+ }
79
+ )
77
80
  end
78
81
 
79
82
  it "should parse location" do
80
- expect Atco.parse_location("QLN700000001252Conway (River Rd) 1\r\n") == {
81
- record_identity: "QL",
82
- transaction_type: "N",
83
- location: "700000001252",
84
- full_location: "Conway (River Rd)",
85
- gazetteer_code: "1"
86
- }
83
+ expect(Atco.parse_location("QLN700000001252Conway (River Rd) 1\r\n")).to eq(
84
+ {
85
+ record_identity: "QL",
86
+ transaction_type: "N",
87
+ location: "700000001252",
88
+ full_location: "Conway (River Rd)",
89
+ gazetteer_code: "1"
90
+ }
91
+ )
87
92
  end
88
93
 
89
94
  # QT7000000012520605 T1F0
90
95
  it "should parse destination" do
91
- expect Atco.parse_destination("QT7000000012520605 T1F0\r\n") == {
92
- record_identity: "QT",
93
- location: "700000001252",
94
- published_arrival_time: "0605",
95
- bay_number: "",
96
- timing_point_indicator: "T1",
97
- fare_stage_indicator: "F0"
98
- }
96
+ expect(Atco.parse_destination("QT7000000012520605 T1F0\r\n")).to eq(
97
+ {
98
+ record_identity: "QT",
99
+ location: "700000001252",
100
+ published_arrival_time: "0605",
101
+ bay_number: "",
102
+ timing_point_indicator: "T1",
103
+ fare_stage_indicator: "F0"
104
+ }
105
+ )
99
106
  end
100
107
 
101
108
  it "should parse intermediate" do
102
- expect Atco.parse_intermediate("QI70000000125607120712B T1F0\r\n") == {
103
- record_identity: "QI",
104
- location: "700000001256",
105
- published_arrival_time: "0712",
106
- published_departure_time: "0712",
107
- activity_flag: "B",
108
- bay_number: "",
109
- timing_point_indicator: "T1",
110
- fare_stage_indicator: "F0"
111
- }
109
+ expect(Atco.parse_intermediate("QI70000000125607120712B T1F0\r\n")).to eq(
110
+ {
111
+ record_identity: "QI",
112
+ location: "700000001256",
113
+ published_arrival_time: "0712",
114
+ published_departure_time: "0712",
115
+ activity_flag: "B",
116
+ bay_number: "",
117
+ timing_point_indicator: "T1",
118
+ fare_stage_indicator: "F0"
119
+ }
120
+ )
112
121
  end
113
122
 
114
123
  it "should parse origin" do
115
- expect Atco.parse_origin("QO7000000012520730 T1F0\r\n") == {
116
- record_identity: "QO",
117
- location: "700000001252",
118
- published_departure_time: "0730",
119
- bay_number: "",
120
- timing_point_indicator: "T1",
121
- fare_stage_indicator: "F0"
122
- }
124
+ expect(Atco.parse_origin("QO7000000012520730 T1F0\r\n")).to eq(
125
+ {
126
+ record_identity: "QO",
127
+ location: "700000001252",
128
+ published_departure_time: "0730",
129
+ bay_number: "",
130
+ timing_point_indicator: "T1",
131
+ fare_stage_indicator: "F0"
132
+ }
133
+ )
123
134
  end
124
135
 
125
136
  it "should parse journey header" do
126
- expect Atco.parse_journey_header("QSNTM 13986520091005 1111100 9A 9018 0 I\r\n") == {
127
- record_identity: "QS",
128
- transaction_type: "N",
129
- operator: "TM",
130
- unique_journey_identifier: "139865",
131
- first_date_of_operation: "20091005",
132
- last_date_of_operation: "",
133
- operates_on_mondays: "1",
134
- operates_on_tuesdays: "1",
135
- operates_on_wednesdays: "1",
136
- operates_on_thursdays: "1",
137
- operates_on_fridays: "1",
138
- operates_on_saturdays: "0",
139
- operates_on_sundays: "0",
140
- school_term_time: "",
141
- bank_holidays: "",
142
- route_number: "9A",
143
- running_board: "9018",
144
- vehicle_type: "0",
145
- registration_number: "",
146
- route_direction: "I"
147
- }
137
+ expect(Atco.parse_journey_header("QSNTM 13986520091005 1111100 9A 9018 0 I\r\n")).to eq(
138
+ {
139
+ record_identity: "QS",
140
+ transaction_type: "N",
141
+ operator: "TM",
142
+ unique_journey_identifier: "139865",
143
+ first_date_of_operation: "20091005",
144
+ last_date_of_operation: "",
145
+ operates_on_mondays: "1",
146
+ operates_on_tuesdays: "1",
147
+ operates_on_wednesdays: "1",
148
+ operates_on_thursdays: "1",
149
+ operates_on_fridays: "1",
150
+ operates_on_saturdays: "0",
151
+ operates_on_sundays: "0",
152
+ school_term_time: "",
153
+ bank_holidays: "",
154
+ route_number: "9A",
155
+ running_board: "9018",
156
+ vehicle_type: "0",
157
+ registration_number: "",
158
+ route_direction: "I"
159
+ }
160
+ )
148
161
  end
149
162
 
150
- describe "with example.cif" do
163
+ describe "with example.cif" do # rubocop:disable Metrics/BlockLength
151
164
  before(:all) do
152
165
  @atco = Atco.parse("spec/fixtures/example.cif")
153
166
  end
154
167
 
155
168
  it "should parse 1 journey" do
156
- expect @atco[:journeys].size == 1
169
+ expect(@atco[:journeys].size).to eq(1)
157
170
  end
158
171
 
159
172
  it "should parse journeys into Atco::Joruney objects" do
160
173
  expect(@atco[:journeys]["139748"]).to be_a_kind_of(Atco::Journey)
161
174
  end
162
175
 
163
- it "should parse 6 stops for joureny 139748" do
164
- expect @atco[:journeys]["139748"].stops.size == 6
176
+ it "should parse 6 stops for journey 139748" do
177
+ expect(@atco[:journeys]["139748"].stops.size).to eq(6)
165
178
  end
166
179
 
167
- it "should parse 6 stops for joureny 139748" do
168
- expect @atco[:journeys]["139748"].stops.size == 6
180
+ it "should parse 2 locations" do
181
+ expect(@atco[:locations].size).to eq(2)
169
182
  end
170
183
 
171
- it "should parse 2 locations" do
172
- expect @atco[:locations].size == 2
184
+ it "should output file as JSON" do
185
+ output = File.join(File.dirname(__FILE__), "artefacts", "test.json")
186
+ File.open(output, "w+") do |f|
187
+ f.flush
188
+ f.write(JSON.pretty_generate(@atco))
189
+ end
190
+
191
+ expect(File.exist?(output)).to be true
192
+
193
+ data = File.read(output)
194
+ json = JSON.parse(data)
195
+ expect(json).to be_a(Hash)
173
196
  end
197
+
198
+ it "should return 17 unparsed lines" do
199
+ expect(@atco[:unparsed].size).to eq(17)
200
+ end
201
+
202
+ it "should not parse GS records" do
203
+ expect(@atco[:unparsed][0]).to eq({
204
+ line: "GS00001433 N Belfast Metro Ops 7000\n",
205
+ line_number: 3
206
+ })
207
+ end
208
+
209
+ it "should not parse GR records" do
210
+ expect(@atco[:unparsed][1]).to eq({
211
+ line: "GR00001433Donegall Square East 7000\n",
212
+ line_number: 4
213
+ })
214
+ end
215
+
174
216
  end
175
217
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "rubygems"
4
- require "rspec"
3
+ require "atco"
5
4
 
6
- require "#{File.dirname(__FILE__)}/../lib/atco"
5
+ RSpec.configure do |config|
6
+ # Enable flags like --only-failures and --next-failure
7
+ config.example_status_persistence_file_path = ".rspec_status"
8
+
9
+ # Disable RSpec exposing methods globally on `Module` and `main`
10
+ config.disable_monkey_patching!
11
+
12
+ config.expect_with :rspec do |c|
13
+ c.syntax = :expect
14
+ end
15
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atco
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Rice
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-11 00:00:00.000000000 Z
11
+ date: 2024-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake