rail_feeds 0.0.1

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