rail_feeds 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +31 -0
  5. data/.travis.yml +26 -0
  6. data/CHANGELOG.md +3 -0
  7. data/Gemfile +6 -0
  8. data/Guardfile +25 -0
  9. data/LICENSE.md +32 -0
  10. data/README.md +77 -0
  11. data/Rakefile +3 -0
  12. data/doc/guides/Logging.md +13 -0
  13. data/doc/guides/Network Rail/CORPUS.md +34 -0
  14. data/doc/guides/Network Rail/SMART.md +39 -0
  15. data/doc/guides/Network Rail/Schedule.md +138 -0
  16. data/file +0 -0
  17. data/lib/rail_feeds/credentials.rb +45 -0
  18. data/lib/rail_feeds/logging.rb +51 -0
  19. data/lib/rail_feeds/network_rail/corpus.rb +77 -0
  20. data/lib/rail_feeds/network_rail/credentials.rb +22 -0
  21. data/lib/rail_feeds/network_rail/http_client.rb +57 -0
  22. data/lib/rail_feeds/network_rail/schedule/association.rb +208 -0
  23. data/lib/rail_feeds/network_rail/schedule/data.rb +215 -0
  24. data/lib/rail_feeds/network_rail/schedule/days.rb +95 -0
  25. data/lib/rail_feeds/network_rail/schedule/fetcher.rb +193 -0
  26. data/lib/rail_feeds/network_rail/schedule/header/cif.rb +102 -0
  27. data/lib/rail_feeds/network_rail/schedule/header/json.rb +79 -0
  28. data/lib/rail_feeds/network_rail/schedule/header.rb +22 -0
  29. data/lib/rail_feeds/network_rail/schedule/parser/cif.rb +141 -0
  30. data/lib/rail_feeds/network_rail/schedule/parser/json.rb +87 -0
  31. data/lib/rail_feeds/network_rail/schedule/parser.rb +108 -0
  32. data/lib/rail_feeds/network_rail/schedule/stp_indicator.rb +72 -0
  33. data/lib/rail_feeds/network_rail/schedule/tiploc.rb +100 -0
  34. data/lib/rail_feeds/network_rail/schedule/train_schedule/change_en_route.rb +158 -0
  35. data/lib/rail_feeds/network_rail/schedule/train_schedule/location/intermediate.rb +119 -0
  36. data/lib/rail_feeds/network_rail/schedule/train_schedule/location/origin.rb +91 -0
  37. data/lib/rail_feeds/network_rail/schedule/train_schedule/location/terminating.rb +72 -0
  38. data/lib/rail_feeds/network_rail/schedule/train_schedule/location.rb +76 -0
  39. data/lib/rail_feeds/network_rail/schedule/train_schedule.rb +392 -0
  40. data/lib/rail_feeds/network_rail/schedule.rb +33 -0
  41. data/lib/rail_feeds/network_rail/smart.rb +186 -0
  42. data/lib/rail_feeds/network_rail/stomp_client.rb +77 -0
  43. data/lib/rail_feeds/network_rail.rb +16 -0
  44. data/lib/rail_feeds/version.rb +14 -0
  45. data/lib/rail_feeds.rb +10 -0
  46. data/rail_feeds.gemspec +32 -0
  47. data/spec/fixtures/network_rail/schedule/data/full.yaml +60 -0
  48. data/spec/fixtures/network_rail/schedule/data/starting.yaml +131 -0
  49. data/spec/fixtures/network_rail/schedule/data/update-gap.yaml +10 -0
  50. data/spec/fixtures/network_rail/schedule/data/update-next.yaml +13 -0
  51. data/spec/fixtures/network_rail/schedule/data/update-old.yaml +10 -0
  52. data/spec/fixtures/network_rail/schedule/data/update.yaml +112 -0
  53. data/spec/fixtures/network_rail/schedule/parser/train_create.json +1 -0
  54. data/spec/fixtures/network_rail/schedule/parser/train_delete.json +1 -0
  55. data/spec/fixtures/network_rail/schedule/train_schedule/json-data.yaml +67 -0
  56. data/spec/rail_feeds/credentials_spec.rb +46 -0
  57. data/spec/rail_feeds/logging_spec.rb +81 -0
  58. data/spec/rail_feeds/network_rail/corpus_spec.rb +92 -0
  59. data/spec/rail_feeds/network_rail/credentials_spec.rb +22 -0
  60. data/spec/rail_feeds/network_rail/http_client_spec.rb +88 -0
  61. data/spec/rail_feeds/network_rail/schedule/association_spec.rb +205 -0
  62. data/spec/rail_feeds/network_rail/schedule/data_spec.rb +219 -0
  63. data/spec/rail_feeds/network_rail/schedule/days_shared.rb +99 -0
  64. data/spec/rail_feeds/network_rail/schedule/days_spec.rb +4 -0
  65. data/spec/rail_feeds/network_rail/schedule/fetcher_spec.rb +228 -0
  66. data/spec/rail_feeds/network_rail/schedule/header/cif_spec.rb +72 -0
  67. data/spec/rail_feeds/network_rail/schedule/header/json_spec.rb +51 -0
  68. data/spec/rail_feeds/network_rail/schedule/header_spec.rb +19 -0
  69. data/spec/rail_feeds/network_rail/schedule/parser/cif_spec.rb +197 -0
  70. data/spec/rail_feeds/network_rail/schedule/parser/json_spec.rb +172 -0
  71. data/spec/rail_feeds/network_rail/schedule/parser_spec.rb +34 -0
  72. data/spec/rail_feeds/network_rail/schedule/stp_indicator_shared.rb +49 -0
  73. data/spec/rail_feeds/network_rail/schedule/stp_indicator_spec.rb +4 -0
  74. data/spec/rail_feeds/network_rail/schedule/tiploc_spec.rb +77 -0
  75. data/spec/rail_feeds/network_rail/schedule/train_schedule/change_en_route_spec.rb +121 -0
  76. data/spec/rail_feeds/network_rail/schedule/train_schedule/location/intermediate_spec.rb +95 -0
  77. data/spec/rail_feeds/network_rail/schedule/train_schedule/location/origin_spec.rb +87 -0
  78. data/spec/rail_feeds/network_rail/schedule/train_schedule/location/terminating_spec.rb +81 -0
  79. data/spec/rail_feeds/network_rail/schedule/train_schedule/location_spec.rb +35 -0
  80. data/spec/rail_feeds/network_rail/schedule/train_schedule_spec.rb +284 -0
  81. data/spec/rail_feeds/network_rail/schedule_spec.rb +41 -0
  82. data/spec/rail_feeds/network_rail/smart_spec.rb +194 -0
  83. data/spec/rail_feeds/network_rail/stomp_client_spec.rb +151 -0
  84. data/spec/rail_feeds/network_rail_spec.rb +7 -0
  85. data/spec/rail_feeds_spec.rb +11 -0
  86. data/spec/spec_helper.rb +47 -0
  87. metadata +282 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bd597c45ef2dc68d4da4278cfeda76ad9ae61e128309071a89c85adb27b510e4
4
+ data.tar.gz: 4af4ee4fb4302f304b444e87804ab6d3813098ccf637fe957b57ac9cbbbeb8f3
5
+ SHA512:
6
+ metadata.gz: 23f62dfd13351944c0c24e204e5c9788a53a4871d03440a5dfae3a121769044c198bc41b7330b3f54a83bb82a4488218b4958b2575797d9d0a637729fef32268
7
+ data.tar.gz: b3e18f1f140f3e16c6c2ad67358cd2269dc4ed43a450384347fed1c73d208353cd13029f2b1c330725f87880be7494e894d2f451baf30a39c806aef9199501a7
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ # Because this is a gem, ignore Gemfile.lock:
2
+
3
+ Gemfile.lock
4
+
5
+ # And because this is Ruby, ignore the following
6
+ # (source: https://github.com/github/gitignore/blob/master/Ruby.gitignore):
7
+
8
+ notes.txt
9
+ todo.txt
10
+ *.gem
11
+ *.rbc
12
+ .bundle
13
+ .config
14
+ coverage
15
+ InstalledFiles
16
+ lib/bundler/man
17
+ pkg
18
+ rdoc
19
+ spec/reports
20
+ test/tmp
21
+ test/version_tmp
22
+ tmp
23
+ gemfiles/*.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --format documentation
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,31 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Style/SignalException:
4
+ Enabled: false
5
+ Metrics/MethodLength:
6
+ Max: 15
7
+ Metrics/AbcSize:
8
+ Max: 17.5
9
+ Metrics/LineLength:
10
+ Max: 90
11
+ Exclude:
12
+ - rail_feeds.gemspec
13
+ - spec/**/*
14
+ Layout/TrailingBlankLines:
15
+ Exclude:
16
+ - rail_feeds.gemspec
17
+ Style/WordArray:
18
+ Exclude:
19
+ - spec/**/*
20
+ Layout/EmptyLines:
21
+ Exclude:
22
+ - spec/**/*
23
+ Metrics/BlockLength:
24
+ Exclude:
25
+ - spec/**/*
26
+ Style/NumericLiterals:
27
+ Exclude:
28
+ - spec/**/*
29
+ Security/YAMLLoad:
30
+ Exclude:
31
+ - spec/**/*
data/.travis.yml ADDED
@@ -0,0 +1,26 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.4.0
4
+ - 2.4.1
5
+ - 2.4.2
6
+ - 2.4.3
7
+ - 2.5.0
8
+ - 2.5.1
9
+ gemfile:
10
+ - Gemfile
11
+ branches:
12
+ only:
13
+ - master
14
+ - staging
15
+ - /gh(?:\d)+(?:-.+)?/
16
+ - /dev_ver_\d+\.\d+/
17
+ before_install: gem update bundler
18
+ script:
19
+ - bundle exec rubocop
20
+ - bundle exec rspec
21
+ notifications:
22
+ slack:
23
+ on_success: always
24
+ on_failure: always
25
+ rooms:
26
+ - secure: CH9idL8/an98uf8uvZ0Pzp7LN9ejdFXh40L6obz5x3tViTzvqo2pqNdryom1E6Dw3NJ7nh4h91XxAq6IZLzrwhSGVGri4tA6EPls6NnkN/UvaK/oCt71ZUaC07hb7SSrc+sQN2CRXMFzxkRBvQ8EKlq+F+Qtj28h0Rebfd79CE+i2NYuAtimYRezOmyLKYUHKRkhYtXbR12FKkNW+2pNNx/Aq02bP/sMXTVdk3k+1bsnpjOoan0m7c2SUkYRD9vmY4u9vxTt2lFFJ+V5NWntXb3y5ttctR9EukULHS2mqFA4q+i6OAJaJUIGWCeWTsB1V3fPuICcZHPV1gF4wVAfV/xees1STJuzGRBpIRTUPd/iXfnNT35e5o97quSHolYx0nCRstonW8IOwlegDqJCbmu8nAaP2M42VkZ4u0Qi1MgzFZ3XcmlLsIrypQHbLnQZKgcnBPxY/8AuqDY9JgiY9RaTMT5xyPox33yAC41MaNYouicyfFSZ4Q+4QFNaPtjaG+N5VR7neMGa5uCXGFHR8H5pWDV5oUFlZzep3bMnrkT9nb/56UjZlAF94nYPEs0nsEm+r35b/g/WWGB+t8UpHzl/jK0s74NbASu+MyNN6X+FiaxOB3WvaFz0wJNqN46SINJjHk1CDhquD1qoyLI+R6AtrRkFvlgaXf6RCSfsWCo=
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## Version 0.0.1
2
+
3
+ * Initial release.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in rail_feeds.gemspec
6
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ guard(
4
+ 'rspec',
5
+ all_on_start: true,
6
+ all_after_pass: true,
7
+ cmd: 'bundle exec rspec'
8
+ ) do
9
+ watch(%r{^spec/.+_spec\.rb$})
10
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch('spec/spec_helper.rb') { 'spec' }
12
+ watch(%r{^spec/.+_shared\.rb$}) { 'spec' }
13
+ end
14
+
15
+ guard(
16
+ 'rubocop',
17
+ all_on_start: true,
18
+ all_after_pass: true,
19
+ notification: true,
20
+ cmd: 'bundle exec rubocop'
21
+ ) do
22
+ watch(%r{^lib/(.+)\.rb$})
23
+ watch(%r{^spec/.+_spec\.rb$})
24
+ watch('.rubocop.yml')
25
+ end
data/LICENSE.md ADDED
@@ -0,0 +1,32 @@
1
+ ##Copyright
2
+ Copyright (c) 2018, Robert Gauld. All rights reserved.
3
+
4
+
5
+ ##License
6
+ This code can be used under the BSD License (reproduced below).
7
+ Commiters to the project give Robert Gauld the right to redistribute their commits
8
+ under the BSD license.
9
+
10
+
11
+ ###BSD License
12
+ Redistribution and use in source and binary forms, with or without modification,
13
+ are permitted provided that the following conditions are met:
14
+
15
+ - Redistributions of source code must retain the above copyright notice,
16
+ this list of conditions and the following disclaimer.
17
+ - Redistributions in binary form must reproduce the above copyright notice,
18
+ this list of conditions and the following disclaimer in the documentation and/or
19
+ other materials provided with the distribution.
20
+ - Neither the name of the project nor the names of its contributors
21
+ may be used to endorse or promote products derived from this software
22
+ without specific prior written permission.
23
+
24
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
25
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
27
+ SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
29
+ OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ [![Gem Version](https://badge.fury.io/rb/rail_feeds.png)](http://badge.fury.io/rb/rail_feeds)
2
+
3
+ Master branch:
4
+ [![Build Status](https://secure.travis-ci.org/robertgauld/rail_feeds.png?branch=master)](http://travis-ci.org/robertgauld/rail_feeds)
5
+ [![Coveralls Status](https://coveralls.io/repos/robertgauld/rail_feeds/badge.png?branch=master)](https://coveralls.io/r/robertgauld/rail_feeds)
6
+ [![Code Climate](https://codeclimate.com/github/robertgauld/rail_feeds.png?branch=master)](https://codeclimate.com/github/robertgauld/rail_feeds)
7
+
8
+ Staging branch:
9
+ [![Build Status](https://secure.travis-ci.org/robertgauld/rail_feeds.png?branch=staging)](http://travis-ci.org/robertgauld/rail_feeds)
10
+ [![Coveralls Status](https://coveralls.io/repos/robertgauld/rail_feeds/badge.png?branch=staging)](https://coveralls.io/r/robertgauld/rail_feeds)
11
+
12
+
13
+ ## Build State
14
+ This project uses continuous integration to help ensure that a quality product is delivered.
15
+ Travis CI monitors two branches (versions) of the code - Master (which is what gets released)
16
+ and Staging (which is what is currently being developed ready for moving to master).
17
+
18
+
19
+ ## Ruby Versions
20
+ This gem supports the following versions of ruby, it may work on other versions but is not tested against them so don't rely on it.
21
+
22
+ * 2.4.0 - 2.4.3
23
+ * 2.5.0 - 2.5.1
24
+
25
+
26
+ ## Rail Feeds
27
+
28
+ Make use of the various open data rails feeds in the UK.
29
+ For more details of what feeds are available visit [The Open Rail Data Wiki](https://wiki.openraildata.com).
30
+
31
+ ## Installation
32
+
33
+ Add to your Gemfile and run the `bundle` command to install it.
34
+
35
+ ```ruby
36
+ gem 'rail_feeds', '~> 0.1'
37
+ ```
38
+
39
+
40
+
41
+ ## Documentation & Versioning
42
+
43
+ Documentation can be found on [rubydoc.info](http://rubydoc.info/github/robertgauld/rail_feeds/master/frames)
44
+ Some guides can be found in the [doc folder of the repo](https://github.com/robertgauld/rail_feeds/tree/master/doc/guides).
45
+
46
+ We follow the [Semantic Versioning](http://semver.org/) concept.
47
+
48
+
49
+ ## Feed Support
50
+
51
+ ### Sources
52
+
53
+ | Source | Module | Module Alias | Support |
54
+ | ------------- | ----------------------- | ------------ | -------------------------- |
55
+ | Network Rail | RailFeeds::NetworkRail | NetRailFeeds | Being developed |
56
+ | National Rail | RailFeeds::NationalRail | NatRailFeeds | None yet - any volunteers? |
57
+
58
+ ### Feeds
59
+
60
+ | Source | Client | Feed | Status |
61
+ | ------------- | ------ | ------------------------------------ | ------------------------------------ |
62
+ | Network Rail | stomp | Real Time Public Performance Measure | Todo |
63
+ | Network Rail | stomp | Temporary Speed Restriction | Todo |
64
+ | Network Rail | stomp | Train Describer | Todo |
65
+ | Network Rail | stomp | Train Movements | Todo |
66
+ | Network Rail | stomp | Very Short Term Planning | Todo |
67
+ | Network Rail | http | Schedule | Can download, fetch, parse and dump. |
68
+ | Network Rail | http | CORPUS (location data) | Can download, fetch and parse. |
69
+ | Network Rail | http | SMART (berth stepping data) | Can download, fetch and parse. |
70
+ | Network Rail | http | Train Planning Data | Todo |
71
+ | Network Rail | http | Train Planning Network Model | Todo |
72
+ | National Rail | stomp | Darwin Push Port | |
73
+ | National Rail | stomp | Darwin Timetable Feed | |
74
+ | National Rail | stomp | Knowledgebase | |
75
+ | National Rail | http | Knowledgebase | |
76
+ | National Rail | soap | Darwin Webservice | |
77
+ | National Rail | rest | Historical Service Performance | |
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
@@ -0,0 +1,13 @@
1
+ # Logging
2
+
3
+ By default events are logged to STDOUT.
4
+
5
+ To replace the logger in use:
6
+ ``` ruby
7
+ RailFeeds::Logging.logger = YOUR NEW LOGGER HERE
8
+ ```
9
+
10
+ To access the logger in use, for example to change the log level:
11
+ ``` ruby
12
+ RailFeeds::Logging.logger ...
13
+ ```
@@ -0,0 +1,34 @@
1
+ # Network Rail CORPUS (Codes for Operations, Retail & Planning – a Unified Solution)
2
+
3
+ The CORPUS data is a list of indentification information for locations around the network.
4
+ See <https://wiki.openraildata.com/index.php/Reference_Data#CORPUS:_Location_Reference_Data>
5
+ for many more details.
6
+
7
+ You'll get an array of a data struct with the following attributes:
8
+
9
+ * tiploc - TIPLOC code (e.g. "ERGNCHP")
10
+ * stanox - STANOX code (e.g. 78370)
11
+ * crs - 3 letter location code (e.g. "ECP")
12
+ * uic - UIC code (e.g. 3750)
13
+ * nlc - NLC code (e.g. "37500")
14
+ * nlc_description - Description of the NLC (e.g. "ENERGLYN & CHURCHILL PARK")
15
+ * nlc_short_description - 16 character version (e.g. "ENERGLYN & C PK")
16
+
17
+ ```ruby
18
+ # Download the CORPUS data and get the data from it
19
+ temp_file = RailFeeds::NetworkRail::CORPUS.fetch
20
+ data = RailFeeds::NetworkRail::CORPUS.load_file(temp_file.path)
21
+
22
+ # Get data from a previously saved file
23
+ data = RailFeeds::NetworkRail::CORPUS.load_file('PATH TO FILE.json.gz')
24
+
25
+ # Get data from a previously saved and extracted file
26
+ data = RailFeeds::NetworkRail::CORPUS.load_file('PATH TO FILE.json')
27
+
28
+ # Get data by fetching it from the web
29
+ RailFeeds::NetworkRail::Credentials.configure(
30
+ username: 'YOUR USERNAME HERE',
31
+ password: 'YOUR PASSWORD HERE'
32
+ )
33
+ data = RailFeeds::NetworkRail::CORPUS.fetch_data
34
+ ```
@@ -0,0 +1,39 @@
1
+ # Network Rail SMART
2
+
3
+ The SMART data is a periodically updated list of mappings between train describer berths
4
+ and locations, allowing for TD events to be translated into arrival/departure from location
5
+ events.
6
+ See <https://wiki.openraildata.com/index.php/Reference_Data#SMART:_Berth_Stepping_Data>
7
+ for many more details.
8
+
9
+ The SMART module allows for two types of data to be fetched:
10
+
11
+ * Step data - This is the data contained in the SMART file and lists the allowable
12
+ steps between berths. You'll get back an array of all the steps.
13
+ * Berth data - Given the previously fetched step data you'll get back an hash in a hash
14
+ of the berths and how they link to other berths. The keys to the outer hash is the signalling
15
+ area of the TD berth and the key to the inner hash is the berth ID. Fir each berth you can
16
+ get an array of the steps in both the up and down direction as well as a list of berth IDs
17
+ reachable in each direction.
18
+
19
+ ```ruby
20
+ # Download the SMART data and get the data from it
21
+ temp_file = RailFeeds::NetworkRail::SMART.fetch
22
+ step_data = RailFeeds::NetworkRail::SMART.load_file(temp_file.path)
23
+
24
+ # Get the SMART data from a previously saved file
25
+ step_data = RailFeeds::NetworkRail::SMART.load_file('PATH TO FILE.json.gz')
26
+
27
+ # Get data from a previously saved and extracted file
28
+ step_data = RailFeeds::NetworkRail::SMART.load_file('PATH TO FILE.json')
29
+
30
+ # Get data by fetching it from the web
31
+ RailFeeds::NetworkRail::Credentials.configure(
32
+ username: 'YOUR USERNAME HERE',
33
+ password: 'YOUR PASSWORD HERE'
34
+ )
35
+ step_data = RailFeeds::NetworkRail::SMART.fetch_data
36
+
37
+ # Get the baerth data
38
+ berth_data = RailFeeds::NetworkRail::SMART.build_berths(step_data)
39
+ ```
@@ -0,0 +1,138 @@
1
+ # Network Rail Schedule
2
+
3
+ The schedule files from Network Rail contain:
4
+
5
+ * Details of the TIPLOCs referenced by trains.
6
+ These are places on the railway for timing purposes.
7
+ * Associations between various trains (e.g. splitting and joining).
8
+ * Train schedules, including details about the train and it's journey.
9
+
10
+ Every Friday a full extract is performed, you should use this to seed your data and then
11
+ apply the following update extracts to keep it current. The CIF format files are generally
12
+ available from about 0100 the morning after (so Saturday for the full extract) whereas the
13
+ JSON format files take longer to prepare are generally available from 0600.
14
+
15
+ You can get various amounts of data at a time, you get to pick between:
16
+ * Just freight traffic
17
+ * A specific TOC (you'll need their [TOC code](https://wiki.openraildata.com/index.php/TOC_Codes))
18
+ * Everything (about 40MB download 400MB extracted file for a full extract)?
19
+
20
+
21
+ ## Fetching files
22
+
23
+ The RailFeeds::NetworkRail::Schedule::Fetcher class can be used to fetch these files
24
+ from Network Rail, you'll need to be registered for their data feeds and have
25
+ subscribed to the schedule you're interested in.
26
+
27
+ ``` ruby
28
+ # 1. Require the gem and configure your credentials.
29
+ require 'rail_feeds'
30
+
31
+ RailFeeds::NetworkRail::Credentials.configure(
32
+ username: 'YOUR USERNAME HERE',
33
+ password: 'YOUR PASSWORD HERE'
34
+ )
35
+
36
+ # 2. Fetch some files
37
+ fetcher = RailFeeds::NetworkRail::Schedule::Fetcher.new
38
+
39
+ fetcher.fetch_all_full(:cif) do |full_file|
40
+ ...
41
+ end
42
+
43
+ fetcher.fetch_all_update('fri', :cif) do |update_file|
44
+ ...
45
+ end
46
+ # Each of the file variables will contain a TempFile which can be used to read
47
+ # the CIF data (or passed to the parser to make use of). The files will be deleted
48
+ # at the end of the block.
49
+ ```
50
+
51
+
52
+ ## Downloading files
53
+
54
+ The RailFeeds::NetworkRail::Schedule::Fetcher class can be used to download these files
55
+ from Network Rail, you'll need to be registered for their data feeds and have
56
+ subscribed to the schedule you're interested in.
57
+
58
+ ``` ruby
59
+ # 1. Require the gem and configure your credentials.
60
+ require 'rail_feeds'
61
+
62
+ RailFeeds::NetworkRail::Credentials.configure(
63
+ username: 'YOUR USERNAME HERE',
64
+ password: 'YOUR PASSWORD HERE'
65
+ )
66
+
67
+ # 2. Download some files
68
+ fetcher = RailFeeds::NetworkRail::Schedule::Fetcher.new
69
+
70
+ fetcher.download_all_full(:cif, 'PATH TO DOWNLOAD TO')
71
+ fetcher.download_all_update('fri', :cif, 'PATH TO DOWNLOAD TO')
72
+
73
+ # Each of the files will be a gzipped version of the CIF formatted schedule.
74
+ ```
75
+
76
+
77
+ ## Parsing CIF files
78
+
79
+ The RailFeeds::NetworkRail::Schedule::Parser class can be used to
80
+ parse the previously fetched files. You can parse several files
81
+ sequentially in one method call. The parser works by calling a
82
+ user provided proc (specific to each event), whenever it meets
83
+ the relevant data in the file. Currently only importing CIF files
84
+ is supported.
85
+
86
+ * on_header(parser, header) - For each header (the first record in a file)
87
+ * on_trailer(parser) - For each trailer (the last record in a file)
88
+ * on_comment(parser, comment) - For each comment line encountered
89
+ * on_tiploc_insert(parser, instruction) - Add a new TIPLOC
90
+ * on_tiploc_amend(parser, instruction) - Amend an existing TIPLOC
91
+ * on_tiploc_delete(parser, instruction) - Delete an existing TIPLOC
92
+ * on_association_new(parser, instruction) - Add a new association
93
+ * on_association_revise(parser, instruction) - Revise an existing association
94
+ * on_association_delete(parser, instruction) - Delete an existing association
95
+ * on_train_new(parser, instruction) - Add a new train schedule
96
+ * on_train_revise(parser, instruction) - Revise an existing train schedule
97
+ * on_train_delete(parser, instruction) - Delete an existing train schedule
98
+
99
+ ``` ruby
100
+ # 3. Parse the fetched files
101
+ parser = RailFeeds::NetworkRail::Schedule::Parser::CIF.new(
102
+ YOUR PROCS HERE
103
+ e.g. on_header: my_header_proc
104
+ )
105
+ fetcher.fetch_all_full(:cif) do |full_file|
106
+ parser.parse_cif full_file
107
+ end
108
+ # Your proc(s) can stop the parsing at anytime by calling parser.stop_parsing
109
+
110
+ # 4. Print all the header information:
111
+ header_proc = proc do |parser, header|
112
+ puts header
113
+ parser.stop_parsing
114
+ end
115
+ parser = RailFeeds::NetworkRail::Schedule::Parser.new(
116
+ on_header: header_proc
117
+ )
118
+ fetcher.fetch_all_full(:cif) do |full_file|
119
+ parser.parse_cif_file full_file
120
+ end
121
+ fetcher.fetch_all_update(:cif) do |update_file|
122
+ parser.parse_cif_file update_file
123
+ end
124
+ ```
125
+
126
+
127
+ ## Just getting the data
128
+ The RailFeeds::NetworkRail::Schedule::Data class can be used to avoid
129
+ directly using the parser and fetcher should you wish. You'll end up
130
+ with the abillity to get arrays of headers, tiplocs, associations and
131
+ trains at the expense of everything being loaded into RAM as once.
132
+
133
+ ``` ruby
134
+ data = RailFeeds::NetworkRail::Schedule::Data.new
135
+ data.fetch_data
136
+ # Uses the fetcher to get the relevant full and/or update files to
137
+ load the most up to date full schedule.
138
+ ```
data/file ADDED
File without changes
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailFeeds
4
+ # A Class to store username & password
5
+ # Can be used to set a global default but create new instances with
6
+ # specific ones for a specific use.
7
+ class Credentials
8
+ # @!attribute [r] username
9
+ # @return [String] The username to use for authentication.
10
+ # @!attribute [r] password
11
+ # @return [String] The password to use for authentication.
12
+ attr_reader :username, :password
13
+
14
+ @username = nil
15
+ @password = nil
16
+
17
+ # Configure default credentials.
18
+ # @param [String] username
19
+ # The username to use for authentication.
20
+ # @param [String] password
21
+ # The password to use for authentication.
22
+ def self.configure(username:, password:)
23
+ @username = username.to_s.clone.freeze
24
+ @password = password.to_s.clone.freeze
25
+ nil
26
+ end
27
+
28
+ # Initialize a new cresential.
29
+ def initialize(
30
+ username: self.class.username,
31
+ password: self.class.password
32
+ )
33
+ @username = username.to_s.clone
34
+ @password = password.to_s.clone
35
+ end
36
+
37
+ def self.username
38
+ @username.clone
39
+ end
40
+
41
+ def self.password
42
+ @password.clone
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module RailFeeds
6
+ # A Module to provide a global logger
7
+ module Logging
8
+ def self.included(base)
9
+ class << base
10
+ # Provide a logger 'attribute' to a class which defaults to the class logger.
11
+ def logger
12
+ @logger || Logging.logger
13
+ end
14
+
15
+ # rubocop:disable Style/TrivialAccessors
16
+ def logger=(logger)
17
+ @logger = logger
18
+ end
19
+ # rubocop:enable Style/TrivialAccessors
20
+ end
21
+ end
22
+
23
+ # Provide a logger attribute to an instance which defaults to the global logger.
24
+ def logger
25
+ @logger || self.class.logger
26
+ end
27
+
28
+ def logger=(logger)
29
+ @logger = logger
30
+ end
31
+
32
+ # Global, memoized, lazy initialized instance of a logger
33
+ def self.logger
34
+ @logger ||= Logger.new(
35
+ STDOUT,
36
+ formatter: formatter,
37
+ level: Logger::DEBUG
38
+ )
39
+ end
40
+
41
+ def self.logger=(logger)
42
+ @logger = logger
43
+ end
44
+
45
+ def self.formatter
46
+ proc do |severity, datetime, progname, message|
47
+ "#{datetime} #{"#{progname} " unless progname.nil?}#{severity}: #{message}\n"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module RailFeeds
6
+ module NetworkRail
7
+ # A module for getting information out of the CORPUS data.
8
+ module CORPUS
9
+ Data = Struct.new(
10
+ :tiploc, :stanox, :crs, :uic, :nlc, :nlc_description, :nlc_short_description
11
+ )
12
+
13
+ # Download the current CORPUS data.
14
+ # @param [RailFeeds::NetworkRail::Credentials] credentials
15
+ # @param [String] file
16
+ # The path to the file to save the .json.gz download in.
17
+ def self.download(file, credentials: Credentials)
18
+ client = HTTPClient.new(credentials: credentials)
19
+ client.download 'ntrod/SupportingFileAuthenticate?type=CORPUS', file
20
+ end
21
+
22
+ # Fetch the current CORPUS data.
23
+ # @param [RailFeeds::NetworkRail::Credentials] credentials
24
+ # @return [Tempfile]
25
+ def self.fetch(credentials: Credentials)
26
+ client = HTTPClient.new(credentials: credentials)
27
+ client.fetch 'ntrod/SupportingFileAuthenticate?type=CORPUS'
28
+ end
29
+
30
+ # Load CORPUS data from either a .json or .json.gz file.
31
+ # @param [String] file The path of the file to open.
32
+ # @return [Array<RailFeeds::NetworkRail::CORPUS::Data>]
33
+ def self.load_file(file)
34
+ Zlib::GzipReader.open(file) do |gz|
35
+ parse_json gz.read
36
+ end
37
+ rescue Zlib::GzipFile::Error
38
+ parse_json File.read(file)
39
+ end
40
+
41
+ # Load CORPUS data from the internet.
42
+ # @param [RailFeeds::NetworkRail::Credentials] credentials
43
+ # The credentials to authenticate with.
44
+ # @return [Array<RailFeeds::NetworkRail::CORPUS::Data>]
45
+ def self.fetch_data(credentials: Credentials)
46
+ client = HTTPClient.new(credentials: credentials)
47
+ client.fetch_unzipped('ntrod/SupportingFileAuthenticate?type=CORPUS') do |file|
48
+ break parse_json file.read
49
+ end
50
+ end
51
+
52
+ # rubocop:disable Metrics/AbcSize
53
+ def self.parse_json(json)
54
+ data = JSON.parse json
55
+ data['TIPLOCDATA'].map do |item|
56
+ Data.new(
57
+ nilify(item['TIPLOC']&.strip),
58
+ nilify(item['STANOX']&.strip)&.to_i,
59
+ nilify(item['3ALPHA']&.strip),
60
+ nilify(item['UIC']&.strip)&.to_i,
61
+ nilify(item['NLC']&.strip),
62
+ nilify(item['NLCDESC']&.strip),
63
+ nilify(item['NLCDESC16']&.strip)
64
+ )
65
+ end
66
+ end
67
+ # rubocop:enable Metrics/AbcSize
68
+ private_class_method :parse_json
69
+
70
+ def self.nilify(item)
71
+ return nil if item.nil? || item.empty?
72
+ item
73
+ end
74
+ private_class_method :nilify
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailFeeds
4
+ module NetworkRail
5
+ # A Class to store username & password required to access network rail feeds
6
+ # Can be used to set a global default but create new instances with
7
+ # specific ones for a specific use.
8
+ class Credentials < RailFeeds::Credentials
9
+ # Get an array of [username, password].
10
+ # @return [Array<String>]
11
+ def to_a
12
+ [username, password]
13
+ end
14
+
15
+ # Get an array of [username, password].
16
+ # @return [Array<String>]
17
+ def self.to_a
18
+ [username, password]
19
+ end
20
+ end
21
+ end
22
+ end