atco 1.0.1 → 1.0.3

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