class RailFeeds::NetworkRail::Schedule::Data

rubocop:disable Metrics/ClassLength A class for holding schedule data read from schedule file(s).

Attributes

associations[RW]

@!attribute [r] last_header The last header added. @return [RailFeeds::NetworkRail::Schedule::Header::CIF] @!attribute [r] associations @return [Hash<RailFeeds::NetworkRail::Schedule::Association>] @!attribute [r] tiplocs @return [Hash<RailFeeds::NetworkRail::Schedule::Tiploc>] @!attribute [r] trains @return [Hash{String=>RailFeeds::NetworkRail::Schedule::TrainSchedule}] Schedules grouped by the train's UID

last_header[RW]

@!attribute [r] last_header The last header added. @return [RailFeeds::NetworkRail::Schedule::Header::CIF] @!attribute [r] associations @return [Hash<RailFeeds::NetworkRail::Schedule::Association>] @!attribute [r] tiplocs @return [Hash<RailFeeds::NetworkRail::Schedule::Tiploc>] @!attribute [r] trains @return [Hash{String=>RailFeeds::NetworkRail::Schedule::TrainSchedule}] Schedules grouped by the train's UID

tiplocs[RW]

@!attribute [r] last_header The last header added. @return [RailFeeds::NetworkRail::Schedule::Header::CIF] @!attribute [r] associations @return [Hash<RailFeeds::NetworkRail::Schedule::Association>] @!attribute [r] tiplocs @return [Hash<RailFeeds::NetworkRail::Schedule::Tiploc>] @!attribute [r] trains @return [Hash{String=>RailFeeds::NetworkRail::Schedule::TrainSchedule}] Schedules grouped by the train's UID

trains[RW]

@!attribute [r] last_header The last header added. @return [RailFeeds::NetworkRail::Schedule::Header::CIF] @!attribute [r] associations @return [Hash<RailFeeds::NetworkRail::Schedule::Association>] @!attribute [r] tiplocs @return [Hash<RailFeeds::NetworkRail::Schedule::Tiploc>] @!attribute [r] trains @return [Hash{String=>RailFeeds::NetworkRail::Schedule::TrainSchedule}] Schedules grouped by the train's UID

Public Class Methods

new(logger: nil) click to toggle source

rubocop:disable Metrics/AbcSize rubocop:disable Metrics/MethodLength Initialize a new data. @param [Logger, nil] logger

The logger for outputting events, if nil the global logger is used.
# File lib/rail_feeds/network_rail/schedule/data.rb, line 28
def initialize(logger: nil)
  self.logger = logger unless logger.nil?
  @parser = Parser::CIF.new(
    logger: logger,
    on_header: proc { |*args| do_header(*args) },
    on_trailer: proc { |*args| do_trailer(*args) },
    on_tiploc_create: proc { |*args| do_tiploc_create(*args) },
    on_tiploc_update: proc { |*args| do_tiploc_update(*args) },
    on_tiploc_delete: proc { |*args| do_tiploc_delete(*args) },
    on_association_create: proc { |*args| do_association_create(*args) },
    on_association_update: proc { |*args| do_association_update(*args) },
    on_association_delete: proc { |*args| do_association_delete(*args) },
    on_train_schedule_create: proc { |*args| do_train_schedule_create(*args) },
    on_train_schedule_update: proc { |*args| do_train_schedule_update(*args) },
    on_train_schedule_delete: proc { |*args| do_train_schedule_delete(*args) }
  )
  reset_data
end

Public Instance Methods

fetch_data(credentials = Credentials) click to toggle source

Fetch data over the web. Gets the feed of all trains. @param [RailFeeds::NetworkRail::Credentials] credentials

The credentials for connecting to the feed.

@return [RailFeeds::NetworkRail::Schedule::Header::CIF]

The header of the last file added.
# File lib/rail_feeds/network_rail/schedule/data.rb, line 95
def fetch_data(credentials = Credentials)
  fetcher = Fetcher.new credentials: credentials

  method = if last_header.nil? ||
              last_header.extracted_at.to_date < Date.today - 6
             # Need to get a full andthen updates
             :fetch_all
           else
             # Can only get updates
             :fetch_all_updates
           end

  fetcher.send(method, :cif) do |file|
    load_cif_file file
  end
end
generate_cif() { |"/!! Start of file\n"| ... } click to toggle source

rubocop:disable Metrics/AbcSize rubocop:disable Metrics/MethodLength Get the contained data in CIF format Expects a block to receive each line

# File lib/rail_feeds/network_rail/schedule/data.rb, line 65
def generate_cif
  fail 'No loaded data' if last_header.nil?

  header = Header::CIF.new(
    extracted_at: last_header.extracted_at,
    update_indicator: 'F',
    start_date: last_header.start_date,
    end_date: last_header.end_date
  )

  yield "/!! Start of file\n"
  yield "/!! Generated: #{header.extracted_at.utc&.strftime('%d/%m/%Y %H:%M')}\n"
  yield header.to_cif
  tiplocs.values.sort.each { |tiploc| yield tiploc.to_cif }
  associations.values.sort.each { |association| yield association.to_cif }
  trains.values.flatten.sort.each do |train_schedule|
    train_schedule.to_cif.each_line { |line| yield line }
  end
  yield "ZZ#{' ' * 78}\n"
  yield "/!! End of file\n"
end
load_cif_file(file) click to toggle source

Load data files into the parser, of types:

* Full CIF file - the data will be replaced
* Update CIF file - the data will be changed

@param [IO] file

The file to load data from.
# File lib/rail_feeds/network_rail/schedule/data.rb, line 54
def load_cif_file(file)
  @parser.parse_file file

  logger.info "Currently have #{associations.count} associations, " \
              "#{tiplocs.count} tiplocs, #{trains.count} trains."
end

Private Instance Methods

do_association_create(_parser, association) click to toggle source

Association New record

# File lib/rail_feeds/network_rail/schedule/data.rb, line 176
def do_association_create(_parser, association)
  associations[association.hash] = association
end
do_association_delete(_parser, association) click to toggle source

Association Delete record

# File lib/rail_feeds/network_rail/schedule/data.rb, line 186
def do_association_delete(_parser, association)
  associations.delete association.hash
end
do_association_update(_parser, association) click to toggle source

Association Revise record

# File lib/rail_feeds/network_rail/schedule/data.rb, line 181
def do_association_update(_parser, association)
  associations[association.hash] = association
end
do_header(_parser, header) click to toggle source

Header record

# File lib/rail_feeds/network_rail/schedule/data.rb, line 154
def do_header(_parser, header)
  ensure_correct_update_order header
  reset_data if header.full?
  @last_header = header
end
do_tiploc_create(_parser, tiploc) click to toggle source

TIPLOC Insert record

# File lib/rail_feeds/network_rail/schedule/data.rb, line 161
def do_tiploc_create(_parser, tiploc)
  tiplocs[tiploc.hash] = tiploc
end
do_tiploc_delete(_parser, tiploc) click to toggle source

TIPLOC Delete record

# File lib/rail_feeds/network_rail/schedule/data.rb, line 171
def do_tiploc_delete(_parser, tiploc)
  tiplocs.delete tiploc.hash
end
do_tiploc_update(_parser, tiploc_id, tiploc) click to toggle source

TIPLOC Amend record

# File lib/rail_feeds/network_rail/schedule/data.rb, line 166
def do_tiploc_update(_parser, tiploc_id, tiploc)
  tiplocs[tiploc_id] = tiploc
end
do_trailer(_parser) click to toggle source

Trailer record

# File lib/rail_feeds/network_rail/schedule/data.rb, line 211
def do_trailer(_parser); end
do_train_schedule_create(_parser, train_schedule) click to toggle source

New Train received

# File lib/rail_feeds/network_rail/schedule/data.rb, line 191
def do_train_schedule_create(_parser, train_schedule)
  trains[train_schedule.uid] ||= []
  trains[train_schedule.uid].push train_schedule
end
do_train_schedule_delete(_parser, train_schedule) click to toggle source

Delete Train record

# File lib/rail_feeds/network_rail/schedule/data.rb, line 206
def do_train_schedule_delete(_parser, train_schedule)
  trains[train_schedule.uid]&.delete train_schedule
end
do_train_schedule_update(parser, train_schedule) click to toggle source

Revise Train received

# File lib/rail_feeds/network_rail/schedule/data.rb, line 197
def do_train_schedule_update(parser, train_schedule)
  trains[train_schedule.uid] ||= []
  index = trains[train_schedule.uid].index train_schedule
  return do_train_schedule_create(parser, train_schedule) if index.nil?

  trains[train_schedule.uid][index] = train_schedule
end
ensure_correct_update_order(header) click to toggle source

rubocop:disable Metrics/AbcSize rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/MethodLength rubocop:disable Metrics/PerceivedComplexity

# File lib/rail_feeds/network_rail/schedule/data.rb, line 125
def ensure_correct_update_order(header)
  if last_header.nil?
    # No data whatsoever - this must be a full extract
    unless header.full?
      fail ArgumentError,
           'Update can\'t be loaded before loading a full extract.'
    end

  elsif last_header.update? && header.update?
    # Check against last update
    if header.extracted_at < last_header.extracted_at
      fail ArgumentError,
           'Update is too old, it is before the last applied update.'
    end
    if header.previous_file_reference != last_header.current_file_reference
      fail ArgumentError,
           'Missing update(s). Last applied update is ' \
           "#{last_header.current_file_reference.inspect}, " \
           "this update requires #{header.previous_file_reference.inspect} " \
           'to be the previous applied update.'
    end
  end
end
reset_data() click to toggle source
# File lib/rail_feeds/network_rail/schedule/data.rb, line 114
def reset_data
  @last_header = nil
  @associations = {}
  @tiplocs = {}
  @trains = {}
end