tube 0.2.5 → 0.3.0pre
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 +7 -0
- data/README.rdoc +9 -4
- data/lib/tube/line.rb +2 -2
- data/lib/tube/station.rb +2 -2
- data/lib/tube/status.rb +26 -31
- data/lib/tube/tfl_client.rb +56 -0
- metadata +13 -30
- data/examples/service_board.rb +0 -39
- data/lib/tube/status_parser.rb +0 -141
- data/test/dummy.html +0 -145
- data/test/status_parser_test.rb +0 -140
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8146af96982affbf00a37dfac0b79cb8e7cd5175
|
4
|
+
data.tar.gz: dbc5fca47bfe1813d1fd46f54569c66871e60a66
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 52ea0e16ce2840d5afbb9e0b7f5fb7a9731168291c9bb8bd818ccf91e24627e2c3279c8bee936989fe483e03d36342a1b5f019c35f92947346e6df5ba05c4357
|
7
|
+
data.tar.gz: 030c5bc541b2c5517f3e1d8c34af46cc9b4bb59cc6abdf7115606237cd342bdba0a3a2cf8fde3b8ecec2714892cadf9f7bf7c0649a5d0bbbb0e38bc756f58316
|
data/README.rdoc
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
= Tube/Status
|
2
2
|
|
3
|
-
A simple MIT licensed Ruby library to access the status of the London
|
3
|
+
A simple MIT licensed Ruby library to access the status of the London
|
4
|
+
Underground network as returned by the Transport for London
|
5
|
+
{API}[http://api.tfl.gov.uk].
|
6
|
+
|
7
|
+
Before the API was available this library scraped the data from the TFL
|
8
|
+
website, at the moment the code has been updated to retain mostly the same Ruby
|
9
|
+
API, while calling the new TFL API, rather than scrape the website. In the
|
10
|
+
future this library may change to better match what TFL now make available.
|
4
11
|
|
5
12
|
== Installation
|
6
13
|
|
7
14
|
$ gem install tube
|
8
15
|
|
9
16
|
== Examples
|
10
|
-
require
|
17
|
+
require "tube/status"
|
11
18
|
|
12
19
|
status = Tube::Status.new
|
13
20
|
|
@@ -32,5 +39,3 @@ A simple MIT licensed Ruby library to access the status of the London Undergroun
|
|
32
39
|
status.reload
|
33
40
|
status.updated.strftime("%I:%M%p")
|
34
41
|
#=> "05:00PM"
|
35
|
-
|
36
|
-
See the {documentation}[http://sourcetagsandcodes.com/codes/tube_status/doc/] for more details.
|
data/lib/tube/line.rb
CHANGED
@@ -12,7 +12,7 @@ module Tube # :nodoc:
|
|
12
12
|
#
|
13
13
|
# Create a new Line.
|
14
14
|
#
|
15
|
-
def initialize(
|
15
|
+
def initialize(id, name, status, problem, message=nil)
|
16
16
|
@id = id
|
17
17
|
@name = name
|
18
18
|
@status = status
|
@@ -21,4 +21,4 @@ module Tube # :nodoc:
|
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
24
|
-
end
|
24
|
+
end
|
data/lib/tube/station.rb
CHANGED
data/lib/tube/status.rb
CHANGED
@@ -1,22 +1,17 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
require
|
4
|
-
require "#{File.dirname( __FILE__ )}/line"
|
5
|
-
require "#{File.dirname( __FILE__ )}/station"
|
1
|
+
require File.expand_path("../tfl_client", __FILE__)
|
2
|
+
require File.expand_path("../line", __FILE__)
|
3
|
+
require File.expand_path("../station", __FILE__)
|
6
4
|
|
7
5
|
module Tube # :nodoc:
|
8
6
|
|
9
|
-
# Models the status of the London Underground network as
|
10
|
-
# http://
|
7
|
+
# Models the status of the London Underground network as returned by
|
8
|
+
# http://api.tfl.gov.uk.
|
11
9
|
#
|
12
|
-
#
|
13
|
-
# to
|
14
|
-
# presentation.
|
15
|
-
# However it is very dynamic, for example should the East London line return
|
16
|
-
# it will show up in the lines array automatically.
|
10
|
+
# Before TFL made an API available this library scraped the TFL website, due
|
11
|
+
# to this the API provided doesn't (currently) match the TFL API very well.
|
17
12
|
#
|
18
13
|
# ==Example Usage
|
19
|
-
# require
|
14
|
+
# require "tube/status"
|
20
15
|
#
|
21
16
|
# status = Tube::Status.new
|
22
17
|
#
|
@@ -50,28 +45,28 @@ module Tube # :nodoc:
|
|
50
45
|
# Request and parse the status of the London Underground network from the
|
51
46
|
# tfl.gov.uk "Live travel news" page.
|
52
47
|
#
|
53
|
-
def initialize(
|
54
|
-
|
55
|
-
|
56
|
-
|
48
|
+
def initialize(app_id=nil, app_key=nil, host="api.tfl.gov.uk", port=80)
|
49
|
+
client = TFLClient.new(app_id, app_key, host, port)
|
50
|
+
line_details = client.line_mode_status(modes: %W{tube dlr overground})
|
51
|
+
station_details = client.stop_point_mode_disruption(modes: %W{tube dlr overground})
|
52
|
+
|
53
|
+
@updated = Time.now
|
57
54
|
|
58
|
-
@lines =
|
59
|
-
id = line[
|
60
|
-
name = line[
|
61
|
-
|
62
|
-
|
63
|
-
|
55
|
+
@lines = line_details.map do |line|
|
56
|
+
id = line["id"].to_sym
|
57
|
+
name = line["name"]
|
58
|
+
status_details = line["lineStatuses"].first
|
59
|
+
status = status_details["statusSeverityDescription"]
|
60
|
+
problem = status_details["statusSeverity"] > 10
|
61
|
+
message = status_details["reason"]
|
64
62
|
|
65
|
-
Line.new(
|
63
|
+
Line.new(id, name, status, problem, message)
|
66
64
|
end
|
67
65
|
|
68
|
-
@station_groups =
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
memo[group[:name]] = stations
|
74
|
-
memo
|
66
|
+
@station_groups = {}
|
67
|
+
station_details.each do |detail|
|
68
|
+
station = Station.new(detail["commonName"], detail["description"])
|
69
|
+
(@station_groups[detail["type"]] ||= []).push(station)
|
75
70
|
end
|
76
71
|
|
77
72
|
self
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Tube
|
5
|
+
class TFLClient
|
6
|
+
|
7
|
+
attr_reader :app_id, :app_key, :host, :port
|
8
|
+
|
9
|
+
def initialize(app_id, app_key, host="api.tfl.gov.uk", port=80)
|
10
|
+
@app_id = app_id
|
11
|
+
@app_key = app_key
|
12
|
+
@host = host
|
13
|
+
@port = port
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.def_request(name, method, path)
|
17
|
+
define_method(name) do |params, body=nil, headers=nil|
|
18
|
+
request(method, path, params, body, headers)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
private_class_method :def_request
|
22
|
+
|
23
|
+
def_request :line_mode_status, :get, "/Line/Mode/{modes}/Status"
|
24
|
+
def_request :stop_point_mode_disruption, :get, "/StopPoint/Mode/{modes}/Disruption"
|
25
|
+
|
26
|
+
def request(method, path, params, body=nil, headers=nil)
|
27
|
+
path = format_path(path, params)
|
28
|
+
Net::HTTP.start(host, port) do |http|
|
29
|
+
req = Net::HTTP.const_get(method.capitalize).new(path, headers)
|
30
|
+
req.body = body if req.request_body_permitted?
|
31
|
+
res = http.request(req)
|
32
|
+
JSON.parse(res.body)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def format_path(path, params)
|
39
|
+
path = path.dup
|
40
|
+
params = {"app_id" => app_id, "app_key" => app_key}.merge(params)
|
41
|
+
params.reject! do |key, value|
|
42
|
+
key = lower_camel_case(key)
|
43
|
+
value = value.join(",") if value.respond_to?(:join)
|
44
|
+
path.sub!("{#{key}}", value.to_s)
|
45
|
+
end
|
46
|
+
path << "?" << params.map {|kv| kv.join("=")}.join("&")
|
47
|
+
end
|
48
|
+
|
49
|
+
def lower_camel_case(string)
|
50
|
+
parts = string.to_s.split("_")
|
51
|
+
first = parts.shift
|
52
|
+
parts.map(&:capitalize).unshift(first.downcase).join("")
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
metadata
CHANGED
@@ -1,27 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tube
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.3.0pre
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Matthew Sadler
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: nokogiri
|
16
|
-
requirement: &2153504660 !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: 1.4.1
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: *2153504660
|
11
|
+
date: 2014-04-08 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
25
13
|
description: A simple Ruby library to access the status of the London Underground
|
26
14
|
network.
|
27
15
|
email: mat@sourcetagsandcodes.com
|
@@ -30,39 +18,34 @@ extensions: []
|
|
30
18
|
extra_rdoc_files:
|
31
19
|
- README.rdoc
|
32
20
|
files:
|
21
|
+
- README.rdoc
|
33
22
|
- lib/tube/line.rb
|
34
23
|
- lib/tube/station.rb
|
35
24
|
- lib/tube/status.rb
|
36
|
-
- lib/tube/
|
37
|
-
- test/dummy.html
|
38
|
-
- test/status_parser_test.rb
|
39
|
-
- examples/service_board.rb
|
40
|
-
- README.rdoc
|
25
|
+
- lib/tube/tfl_client.rb
|
41
26
|
homepage: http://github.com/matsadler/tube
|
42
27
|
licenses: []
|
28
|
+
metadata: {}
|
43
29
|
post_install_message:
|
44
30
|
rdoc_options:
|
45
|
-
- --main
|
31
|
+
- "--main"
|
46
32
|
- README.rdoc
|
47
33
|
require_paths:
|
48
34
|
- lib
|
49
35
|
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
36
|
requirements:
|
52
|
-
- -
|
37
|
+
- - ">="
|
53
38
|
- !ruby/object:Gem::Version
|
54
39
|
version: '0'
|
55
40
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
-
none: false
|
57
41
|
requirements:
|
58
|
-
- -
|
42
|
+
- - ">"
|
59
43
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
44
|
+
version: 1.3.1
|
61
45
|
requirements: []
|
62
46
|
rubyforge_project:
|
63
|
-
rubygems_version:
|
47
|
+
rubygems_version: 2.2.0
|
64
48
|
signing_key:
|
65
|
-
specification_version:
|
49
|
+
specification_version: 4
|
66
50
|
summary: Access the status of the London Underground network.
|
67
|
-
test_files:
|
68
|
-
- test/status_parser_test.rb
|
51
|
+
test_files: []
|
data/examples/service_board.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'tube/status'
|
3
|
-
require 'terminal_color/ansi'
|
4
|
-
|
5
|
-
class String
|
6
|
-
include Terminal::Color::ANSI
|
7
|
-
end
|
8
|
-
|
9
|
-
LINE_COLORS = {
|
10
|
-
:bakerloo => [:bright_white, :red_bg],
|
11
|
-
:central => [:white, :bright_red_bg],
|
12
|
-
:circle => [:blue, :bright_yellow_bg],
|
13
|
-
:district => [:bright_white, :green_bg],
|
14
|
-
:hammersmithandcity => [:blue, :bright_magenta_bg],
|
15
|
-
:jubilee => [:bright_white, :bright_black_bg],
|
16
|
-
:metropolitan => [:bright_white, :magenta_bg],
|
17
|
-
:northern => [:bright_white, :black_bg],
|
18
|
-
:piccadilly => [:bright_white, :blue_bg],
|
19
|
-
:victoria => [:bright_white, :bright_blue_bg],
|
20
|
-
:waterlooandcity => [:blue, :bright_cyan_bg],
|
21
|
-
:dlr => [:bright_white, :cyan_bg],
|
22
|
-
:overground => [:bright_white, :yellow_bg]
|
23
|
-
}
|
24
|
-
STATUS_COLOR = [:blue, :bright_white_bg]
|
25
|
-
PROBLEM_COLOR = STATUS_COLOR + [:negative]
|
26
|
-
|
27
|
-
status = Tube::Status.new
|
28
|
-
|
29
|
-
longest_name = status.lines.map {|l| l.name.length}.max
|
30
|
-
longest_status = status.lines.map {|l| l.status.length}.max
|
31
|
-
formatted_time = status.updated.strftime("%I:%M%p").downcase.sub(/^0/, "")
|
32
|
-
|
33
|
-
puts " Live travel news".style(:bold), " Last update: #{formatted_time}", ""
|
34
|
-
status.lines.each do |line|
|
35
|
-
print " "
|
36
|
-
printf(" %-#{longest_name}s ".color(*LINE_COLORS[line.id]), line.name)
|
37
|
-
status_color = if line.problem? then PROBLEM_COLOR else STATUS_COLOR end
|
38
|
-
printf(" %-#{longest_status}s \n".color(*status_color), line.status)
|
39
|
-
end
|
data/lib/tube/status_parser.rb
DELETED
@@ -1,141 +0,0 @@
|
|
1
|
-
require 'time'
|
2
|
-
require 'date'
|
3
|
-
require 'rubygems'
|
4
|
-
require 'nokogiri'
|
5
|
-
|
6
|
-
module Tube # :nodoc:
|
7
|
-
module StatusParser # :nodoc:
|
8
|
-
extend self
|
9
|
-
|
10
|
-
def parse( html_doc )
|
11
|
-
html_doc.gsub!( / /, " " )
|
12
|
-
doc = Nokogiri::HTML( html_doc )
|
13
|
-
|
14
|
-
updated_element = doc.at_css( "div.hd-row > h2" )
|
15
|
-
updated = parse_updated( updated_element )
|
16
|
-
|
17
|
-
service_board = doc.at_css( "#service-board" )
|
18
|
-
|
19
|
-
line_elements = service_board.css( "ul#lines > li.ltn-line" )
|
20
|
-
lines = line_elements.map( &method( :parse_line ) )
|
21
|
-
|
22
|
-
station_group_elements = service_board.css( "ul#stations > li" )
|
23
|
-
station_groups = station_group_elements.map(&method(:parse_station_group))
|
24
|
-
|
25
|
-
{:updated => updated, :lines => lines, :station_groups => station_groups}
|
26
|
-
end
|
27
|
-
|
28
|
-
def parse_updated( updated_element )
|
29
|
-
time_text = updated_element.content.match( /(\d?\d:\d\d)/ )[0]
|
30
|
-
time_zone = if is_bst? then "+0100" else "+0000" end
|
31
|
-
|
32
|
-
Time.parse( "#{time_text} #{time_zone}" )
|
33
|
-
end
|
34
|
-
|
35
|
-
def parse_line( line_element )
|
36
|
-
name_element = line_element.at_css( "h3.ltn-name" )
|
37
|
-
name = name_element.content
|
38
|
-
html_class = name_element["class"].split.first
|
39
|
-
status = parse_status( line_element.at_css( "div.status" ) )
|
40
|
-
|
41
|
-
{:name => name, :html_class => html_class, :status => status}
|
42
|
-
end
|
43
|
-
|
44
|
-
def parse_status( status_element )
|
45
|
-
header = status_element.at_css( "h4.ltn-title" )
|
46
|
-
|
47
|
-
if header
|
48
|
-
headline = header.content.strip
|
49
|
-
message = parse_status_message( status_element.css("div.message > p") )
|
50
|
-
else
|
51
|
-
headline = status_element.content.strip
|
52
|
-
end
|
53
|
-
problem = status_element["class"].split.include?( "problem" )
|
54
|
-
|
55
|
-
{:headline => headline, :problem => problem, :message => message}
|
56
|
-
end
|
57
|
-
|
58
|
-
def parse_station_group( station_group_element )
|
59
|
-
name = station_group_element.at_css( "h3" ).content
|
60
|
-
|
61
|
-
station_elements = station_group_element.css( "ul > li.ltn-station" )
|
62
|
-
|
63
|
-
stations = station_elements.map do |station_element|
|
64
|
-
parse_station( station_element )
|
65
|
-
end
|
66
|
-
|
67
|
-
{:name => name, :stations => stations}
|
68
|
-
end
|
69
|
-
|
70
|
-
def parse_station( station_element )
|
71
|
-
name = station_element.at_css( "h4.ltn-name" ).content.strip
|
72
|
-
message = parse_status_message( station_element.css( "div.message > p" ) )
|
73
|
-
|
74
|
-
{:name => name, :message => message}
|
75
|
-
end
|
76
|
-
|
77
|
-
def parse_status_message( messages )
|
78
|
-
text_messages = messages.map do |message|
|
79
|
-
if message.children
|
80
|
-
message.children.select {|child| child.text?}.join( " " )
|
81
|
-
end
|
82
|
-
end.compact
|
83
|
-
text_messages.reject! {|m| m.empty?}
|
84
|
-
|
85
|
-
text_messages.map {|m| m.gsub( /\s+/, " " ).strip}.join( "\n" )
|
86
|
-
end
|
87
|
-
|
88
|
-
private
|
89
|
-
|
90
|
-
# :call-seq: is_bst? -> bool
|
91
|
-
#
|
92
|
-
# Is British Summer Time currently in effect.
|
93
|
-
#
|
94
|
-
def is_bst?
|
95
|
-
bst_start = last_sunday_of_month( "march" )
|
96
|
-
bst_end = last_sunday_of_month( "october" )
|
97
|
-
|
98
|
-
one_hour = 3600
|
99
|
-
|
100
|
-
bst_start = Time.gm( bst_start.year, bst_start.month, bst_start.day )
|
101
|
-
bst_start += one_hour
|
102
|
-
|
103
|
-
bst_end = Time.gm( bst_end.year, bst_end.month, bst_end.day )
|
104
|
-
bst_end += one_hour
|
105
|
-
|
106
|
-
bst = (bst_start..bst_end)
|
107
|
-
if bst.respond_to?(:cover?)
|
108
|
-
bst.cover?( Time.now.getgm )
|
109
|
-
else
|
110
|
-
bst.include?( Time.now.getgm )
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# :call-seq: last_sunday_of_month(month_name) -> date
|
115
|
-
#
|
116
|
-
def last_sunday_of_month( month )
|
117
|
-
start_of_next_month = Date.parse( next_month_name( month ) )
|
118
|
-
|
119
|
-
week_day = start_of_next_month.wday
|
120
|
-
|
121
|
-
distance_from_sunday = if week_day == 0 then 7 else week_day end
|
122
|
-
start_of_next_month - distance_from_sunday
|
123
|
-
end
|
124
|
-
|
125
|
-
# :call-seq: next_month_name(month_name) -> string
|
126
|
-
#
|
127
|
-
def next_month_name( month )
|
128
|
-
index = Date::MONTHNAMES.index( month.capitalize )
|
129
|
-
index ||= ABBR_MONTHNAMES.index( month.capitalize )
|
130
|
-
|
131
|
-
index += 1
|
132
|
-
|
133
|
-
if index >= 12
|
134
|
-
index = 1
|
135
|
-
end
|
136
|
-
|
137
|
-
Date::MONTHNAMES[index]
|
138
|
-
end
|
139
|
-
|
140
|
-
end
|
141
|
-
end
|
data/test/dummy.html
DELETED
@@ -1,145 +0,0 @@
|
|
1
|
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
2
|
-
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
3
|
-
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
4
|
-
<head>
|
5
|
-
<title>Service update now | Transport for London</title>
|
6
|
-
<!-- Transport for London website template version 1.7 -->
|
7
|
-
|
8
|
-
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
|
9
|
-
</head>
|
10
|
-
<body class="template-6">
|
11
|
-
<!-- <lm>Sun, 15 Nov 2009 12:12:20 GMT</lm> -->
|
12
|
-
|
13
|
-
<div id="container" class="live-travel-news">
|
14
|
-
<div id="main-content">
|
15
|
-
<h1>Live travel news</h1>
|
16
|
-
|
17
|
-
<div class="hd-row">
|
18
|
-
<h2>
|
19
|
-
Service update at 12:12
|
20
|
-
<a href="later.html" id="works_later" class="alert">
|
21
|
-
View engineering works planned for <strong>later today</strong>
|
22
|
-
</a>
|
23
|
-
</h2>
|
24
|
-
|
25
|
-
<div id="closures-email">
|
26
|
-
<h3>
|
27
|
-
<a class="iframe closures-eml-lnk" href="/plannedworks">
|
28
|
-
Weekend closures email
|
29
|
-
</a>
|
30
|
-
</h3>
|
31
|
-
<p>Get advance warning of weekend closures</p>
|
32
|
-
</div>
|
33
|
-
</div>
|
34
|
-
|
35
|
-
<div class="short-form">
|
36
|
-
<div class="clear"></div>
|
37
|
-
</div>
|
38
|
-
|
39
|
-
<div id="service-board">
|
40
|
-
<h2>Tube lines</h2>
|
41
|
-
<ul id="lines">
|
42
|
-
<li class="ltn-line">
|
43
|
-
<h3 class="central ltn-name">Central</h3>
|
44
|
-
<div class="status">Good service</div>
|
45
|
-
</li>
|
46
|
-
|
47
|
-
<li class="ltn-line">
|
48
|
-
<h3 class="district ltn-name">District</h3>
|
49
|
-
<div class="problem status">
|
50
|
-
<h4 class="ltn-title">Part closure</h4>
|
51
|
-
<div class="message">
|
52
|
-
<p>Rail replacement bus</p>
|
53
|
-
<p>Service A: details...</p>
|
54
|
-
<dl class="linklist">
|
55
|
-
<dd>
|
56
|
-
<a href="http://journeyplanner.tfl.gov.uk/">Replan your journey</a>
|
57
|
-
</dd>
|
58
|
-
</dl>
|
59
|
-
</div>
|
60
|
-
</div>
|
61
|
-
</li>
|
62
|
-
|
63
|
-
<li class="ltn-line">
|
64
|
-
<h3 class="waterlooandcity ltn-name">Waterloo & City</h3>
|
65
|
-
<div class="problem status">
|
66
|
-
<h4 class="ltn-title">Planned closure</h4>
|
67
|
-
<div class="message">
|
68
|
-
<p>Closed Sunday.</p>
|
69
|
-
<dl class="linklist">
|
70
|
-
<dd>
|
71
|
-
<a href="http://journeyplanner.tfl.gov.uk/">Replan your journey</a>
|
72
|
-
</dd>
|
73
|
-
</dl>
|
74
|
-
</div>
|
75
|
-
</div>
|
76
|
-
</li>
|
77
|
-
</ul>
|
78
|
-
|
79
|
-
<h2>Tube stations</h2>
|
80
|
-
<ul id="stations">
|
81
|
-
<li class="ltn-closed">
|
82
|
-
<h3>Closed stations</h3>
|
83
|
-
<ul>
|
84
|
-
<li class="ltn-station" id="stop-10000xx">
|
85
|
-
<h4 class="ltn-name">Bank</h4>
|
86
|
-
<div class="message">
|
87
|
-
<p>Closed due to excessive noise.</p>
|
88
|
-
<dl class="linklist">
|
89
|
-
<dd>
|
90
|
-
Replan your journey
|
91
|
-
<a href="journeyplanner">to</a>
|
92
|
-
/
|
93
|
-
<a href="http://journeyplanner">from</a>
|
94
|
-
this station
|
95
|
-
</dd>
|
96
|
-
</dl>
|
97
|
-
</div>
|
98
|
-
</li>
|
99
|
-
|
100
|
-
<li class="ltn-station" id="stop-10000xx">
|
101
|
-
<h4 class="ltn-name">Holborn</h4>
|
102
|
-
<div class="message">
|
103
|
-
<p>Closed due to fire investigation.</p>
|
104
|
-
<p>Go to Chancery Lane or Tottenham Court Road instead.</p>
|
105
|
-
<dl class="linklist">
|
106
|
-
<dd>
|
107
|
-
Replan your journey
|
108
|
-
<a href="journeyplanner">to</a>
|
109
|
-
/
|
110
|
-
<a href="http://journeyplanner">from</a>
|
111
|
-
this station
|
112
|
-
</dd>
|
113
|
-
</dl>
|
114
|
-
</div>
|
115
|
-
</li>
|
116
|
-
</ul>
|
117
|
-
</li>
|
118
|
-
|
119
|
-
<li>
|
120
|
-
<h3>Station maintenance</h3>
|
121
|
-
<p class="ltn-more-info">(lifts, escalators etc.)</p>
|
122
|
-
<ul>
|
123
|
-
<li class="ltn-station" id="stop-10000xx">
|
124
|
-
<h4 class="ltn-name">Elephant & Castle</h4>
|
125
|
-
<div class="message">
|
126
|
-
<p>Reduced lift service.</p>
|
127
|
-
<dl class="linklist">
|
128
|
-
<dd>
|
129
|
-
Replan your journey
|
130
|
-
<a href="journeyplanner">to</a>
|
131
|
-
/
|
132
|
-
<a href="http://journeyplanner">from</a>
|
133
|
-
this station
|
134
|
-
</dd>
|
135
|
-
</dl>
|
136
|
-
</div>
|
137
|
-
</li>
|
138
|
-
</ul>
|
139
|
-
</li>
|
140
|
-
</ul>
|
141
|
-
</div>
|
142
|
-
</div>
|
143
|
-
</div>
|
144
|
-
</body>
|
145
|
-
</html>
|
data/test/status_parser_test.rb
DELETED
@@ -1,140 +0,0 @@
|
|
1
|
-
require "test/unit"
|
2
|
-
|
3
|
-
require File.expand_path("../../lib/tube/status", __FILE__)
|
4
|
-
|
5
|
-
class TestStatusParser < Test::Unit::TestCase
|
6
|
-
def test_parse_updated
|
7
|
-
document = Nokogiri::HTML("<h2>Service update at 18:26</h2>")
|
8
|
-
element = document.at_css("h2")
|
9
|
-
result = Tube::StatusParser.parse_updated(element)
|
10
|
-
|
11
|
-
assert_equal(Time.parse("6:26pm"), result)
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_parse_updated_with_anchor_in_element
|
15
|
-
document = Nokogiri::HTML(%Q{<h2>Service update at 12:12 <a href="/later.html">View engineering works planned for later today</a></h2>})
|
16
|
-
element = document.at_css("h2")
|
17
|
-
result = Tube::StatusParser.parse_updated(element)
|
18
|
-
|
19
|
-
assert_equal(Time.parse("12:12pm"), result)
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_parse_line
|
23
|
-
document = Nokogiri::HTML(%Q{<li class="ltn-line"><h3 class="central ltn-name">Central</h3><div class="status">Good service</div></li>})
|
24
|
-
element = document.at_css("li")
|
25
|
-
result = Tube::StatusParser.parse_line(element)
|
26
|
-
|
27
|
-
assert_equal("Central", result[:name])
|
28
|
-
assert_equal("central", result[:html_class])
|
29
|
-
assert_equal("Good service", result[:status][:headline])
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_parse_line_with_complex_name
|
33
|
-
document = Nokogiri::HTML(%Q{<li class="ltn-line"><h3 class="waterlooandcity ltn-name">Waterloo & City</h3><div class="status">Good service</div></li>})
|
34
|
-
element = document.at_css("li")
|
35
|
-
result = Tube::StatusParser.parse_line(element)
|
36
|
-
|
37
|
-
assert_equal("Waterloo & City", result[:name])
|
38
|
-
assert_equal("waterlooandcity", result[:html_class])
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_parse_status
|
42
|
-
document = Nokogiri::HTML(%Q{<div class="status">Good service</div>})
|
43
|
-
element = document.at_css("div")
|
44
|
-
result = Tube::StatusParser.parse_status(element)
|
45
|
-
|
46
|
-
assert_equal("Good service", result[:headline])
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_parse_status_with_problem
|
50
|
-
document = Nokogiri::HTML(%Q{<div class="problem status">Part suspended</div>})
|
51
|
-
element = document.at_css("div")
|
52
|
-
result = Tube::StatusParser.parse_status(element)
|
53
|
-
|
54
|
-
assert_equal(true, result[:problem])
|
55
|
-
end
|
56
|
-
|
57
|
-
def test_parse_status_with_header
|
58
|
-
document = Nokogiri::HTML(%Q{<div class="status"><h4 class="ltn-title">Part suspended</h4></div>})
|
59
|
-
element = document.at_css("div")
|
60
|
-
result = Tube::StatusParser.parse_status(element)
|
61
|
-
|
62
|
-
assert_equal("Part suspended", result[:headline])
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_parse_status_with_message
|
66
|
-
document = Nokogiri::HTML(%Q{<div class="problem status"><h4 class="ltn-title">Part closure</h4><div class="message"><p>engineering works, etc...</p></div></div>})
|
67
|
-
element = document.at_css("div.status")
|
68
|
-
result = Tube::StatusParser.parse_status(element)
|
69
|
-
|
70
|
-
assert_equal("Part closure", result[:headline])
|
71
|
-
assert_equal(true, result[:problem])
|
72
|
-
assert_equal("engineering works, etc...", result[:message])
|
73
|
-
end
|
74
|
-
|
75
|
-
def test_parse_status_message
|
76
|
-
document = Nokogiri::HTML(%Q{<div><p>engineering works, etc...</p></div>})
|
77
|
-
elements = document.css("div p")
|
78
|
-
result = Tube::StatusParser.parse_status_message(elements)
|
79
|
-
|
80
|
-
assert_equal("engineering works, etc...", result)
|
81
|
-
end
|
82
|
-
|
83
|
-
def test_parse_status_message_with_multi_paragraph_message
|
84
|
-
document = Nokogiri::HTML(%Q{<div><p>Rail replacement bus</p><p>Service A: details...</p></div>})
|
85
|
-
elements = document.css("div p")
|
86
|
-
result = Tube::StatusParser.parse_status_message(elements)
|
87
|
-
|
88
|
-
assert_equal("Rail replacement bus\nService A: details...", result)
|
89
|
-
end
|
90
|
-
|
91
|
-
def test_parse_status_message_removes_anchor_from_message
|
92
|
-
document = Nokogiri::HTML(%Q{<div><p>Closed Sunday.</p><p><a href="/projectsandschemes"><p>Click for further information.</p></a></p></div>})
|
93
|
-
elements = document.css("div > p")
|
94
|
-
result = Tube::StatusParser.parse_status_message(elements)
|
95
|
-
|
96
|
-
assert_equal("Closed Sunday.", result)
|
97
|
-
end
|
98
|
-
|
99
|
-
def test_parse_station_group
|
100
|
-
document = Nokogiri::HTML(%Q{<li class="ltn-closed"><h3>Closed stations</h3><ul>
|
101
|
-
<li class="ltn-station" id="stop-1000013"><h4 class="ltn-name">Bank</h4><div class="message"><p>Closed due to excessive noise.</p></div></li>
|
102
|
-
<li class="ltn-station" id="stop-10000xx"><h4 class="ltn-name">Holborn</h4><div class="message"><p>Closed due to fire investigation.</p></div></li></ul></li>})
|
103
|
-
element = document.at_css("li.ltn-closed")
|
104
|
-
result = Tube::StatusParser.parse_station_group(element)
|
105
|
-
|
106
|
-
assert_equal("Closed stations", result[:name])
|
107
|
-
assert_equal("Bank", result[:stations].first[:name])
|
108
|
-
assert_equal("Holborn", result[:stations].last[:name])
|
109
|
-
end
|
110
|
-
|
111
|
-
def test_parse_station
|
112
|
-
document = Nokogiri::HTML(%Q{<li class="ltn-station" id="stop-1000013"><h4 class="ltn-name">Bank</h4><div class="message"><p>Closed due to excessive noise.</p></div></li>})
|
113
|
-
element = document.at_css("li")
|
114
|
-
result = Tube::StatusParser.parse_station(element)
|
115
|
-
|
116
|
-
assert_equal("Bank", result[:name])
|
117
|
-
# This seriously happend once.
|
118
|
-
assert_equal("Closed due to excessive noise.", result[:message])
|
119
|
-
end
|
120
|
-
|
121
|
-
def test_parse
|
122
|
-
# the file used here is an approximation of the most important bits of the
|
123
|
-
# Live travel news at http://www.tfl.gov.uk/tfl/livetravelnews/realtime/tube/default.html
|
124
|
-
document = open("#{File.dirname( __FILE__ )}/dummy.html").read
|
125
|
-
result = Tube::StatusParser.parse(document)
|
126
|
-
|
127
|
-
assert(result)
|
128
|
-
assert_equal(Time.parse("12:12pm"), result[:updated])
|
129
|
-
assert_equal(3, result[:lines].length)
|
130
|
-
assert_equal("Central", result[:lines].first[:name])
|
131
|
-
assert_equal("Closed Sunday.", result[:lines].last[:status][:message])
|
132
|
-
assert_equal(2, result[:station_groups].length)
|
133
|
-
assert_equal(2, result[:station_groups].first[:stations].length)
|
134
|
-
assert_equal(1, result[:station_groups].last[:stations].length)
|
135
|
-
assert_equal("Closed stations", result[:station_groups].first[:name])
|
136
|
-
assert_equal("Elephant & Castle", result[:station_groups].last[:stations].first[:name])
|
137
|
-
|
138
|
-
assert_equal({:updated=>Time.parse("12:12pm"), :station_groups=>[{:stations=>[{:message=>"Closed due to excessive noise.", :name=>"Bank"}, {:message=>"Closed due to fire investigation.\nGo to Chancery Lane or Tottenham Court Road instead.", :name=>"Holborn"}], :name=>"Closed stations"}, {:stations=>[{:message=>"Reduced lift service.", :name=>"Elephant & Castle"}], :name=>"Station maintenance"}], :lines=>[{:status=>{:problem=>false, :message=>nil, :headline=>"Good service"}, :html_class=>"central", :name=>"Central"}, {:status=>{:problem=>true, :message=>"Rail replacement bus\nService A: details...", :headline=>"Part closure"}, :html_class=>"district", :name=>"District"}, {:status=>{:problem=>true, :message=>"Closed Sunday.", :headline=>"Planned closure"}, :html_class=>"waterlooandcity", :name=>"Waterloo & City"}]}, result)
|
139
|
-
end
|
140
|
-
end
|