matsadler-tube 0.1.0
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.
- data/lib/line.rb +72 -0
- data/lib/line_group.rb +28 -0
- data/lib/station.rb +61 -0
- data/lib/station_group.rb +65 -0
- data/lib/station_group_group.rb +62 -0
- data/lib/status.rb +317 -0
- data/lib/tube.rb +13 -0
- metadata +83 -0
data/lib/line.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
module Tube # :nodoc:
|
2
|
+
|
3
|
+
# Models the data gathered on a tube line from the tfl.gov.uk "Live travel
|
4
|
+
# news" page.
|
5
|
+
#
|
6
|
+
class Line
|
7
|
+
attr_reader :id
|
8
|
+
attr_accessor :status, :problem, :message, :name
|
9
|
+
alias_method :problem?, :problem
|
10
|
+
|
11
|
+
# :call-seq: Line.new(id, status, problem=false, message=nil, name=nil)
|
12
|
+
#
|
13
|
+
# Create a new Line. If name is ommited it will be set to id.
|
14
|
+
#
|
15
|
+
def initialize( id, status, problem=false, message=nil, name=nil )
|
16
|
+
@id = id
|
17
|
+
@status = status
|
18
|
+
@problem = problem
|
19
|
+
@message = message
|
20
|
+
@name = name || @id
|
21
|
+
end
|
22
|
+
|
23
|
+
# :call-seq: line.to_hash -> hash
|
24
|
+
#
|
25
|
+
# Returns a hash representation of the object.
|
26
|
+
# {"id" => "central", "status" => "Good service", "problem" => false,
|
27
|
+
# "message" => nil, "name" => "Central"}
|
28
|
+
#
|
29
|
+
#
|
30
|
+
def to_hash
|
31
|
+
instance_variables.inject( {} ) do |memo, var|
|
32
|
+
memo.merge( {var[1..-1] => instance_variable_get( var )} )
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# :call-seq: line.to_json -> string
|
37
|
+
#
|
38
|
+
# Returns a string of JSON representing the object.
|
39
|
+
# '{"id":"central", "status":"Good service", "problem":false,
|
40
|
+
# "message":null, "name":"Central"}'
|
41
|
+
#
|
42
|
+
def to_json( *args )
|
43
|
+
to_hash.to_json( *args )
|
44
|
+
end
|
45
|
+
|
46
|
+
# :call-seq: line.to_xml -> string
|
47
|
+
# line.to_xml(false) -> rexml_document.
|
48
|
+
#
|
49
|
+
# Returns a string of XML representing the object.
|
50
|
+
# <line>
|
51
|
+
# <id>central</id>
|
52
|
+
# <status>Good service</status>
|
53
|
+
# <problem>false</problem>
|
54
|
+
# <message/>
|
55
|
+
# <name>Central</name>
|
56
|
+
# </line>
|
57
|
+
#
|
58
|
+
# Alternately pass false as the only argument to get an instance of
|
59
|
+
# REXML::Document.
|
60
|
+
#
|
61
|
+
def to_xml( as_string=true )
|
62
|
+
doc = REXML::Document.new
|
63
|
+
root = doc.add_element( "line" )
|
64
|
+
instance_variables.each do |var|
|
65
|
+
el = root.add_element( var[1..-1] )
|
66
|
+
el.add_text( instance_variable_get( var ).to_s )
|
67
|
+
end
|
68
|
+
if as_string then doc.to_s else doc end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
data/lib/line_group.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Tube # :nodoc:
|
2
|
+
|
3
|
+
# Really just an array with an appropriate #to_xml method.
|
4
|
+
#
|
5
|
+
class LineGroup < Array
|
6
|
+
|
7
|
+
# :call-seq: line_group.to_xml -> string
|
8
|
+
# line_group.to_xml(false) -> rexml_document.
|
9
|
+
#
|
10
|
+
# Returns a string of XML representing the object.
|
11
|
+
# <lines>
|
12
|
+
# contents of the array as xml...
|
13
|
+
# </lines>
|
14
|
+
#
|
15
|
+
# Alternately pass false as the only argument to get an instance of
|
16
|
+
# REXML::Document.
|
17
|
+
#
|
18
|
+
def to_xml( as_string=true )
|
19
|
+
doc = REXML::Document.new
|
20
|
+
root = doc.add_element( "lines" )
|
21
|
+
each do |e|
|
22
|
+
root.add_element( e.to_xml( false ) )
|
23
|
+
end
|
24
|
+
if as_string then doc.to_s else doc end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
data/lib/station.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Tube # :nodoc:
|
2
|
+
|
3
|
+
# Models the data gathered on a tube station from the tfl.gov.uk "Live travel
|
4
|
+
# news" page.
|
5
|
+
#
|
6
|
+
class Station
|
7
|
+
attr_reader :name
|
8
|
+
attr_accessor :message
|
9
|
+
|
10
|
+
# :call-seq: Station.new(name, message=nil)
|
11
|
+
#
|
12
|
+
# Create a new Station.
|
13
|
+
#
|
14
|
+
def initialize( name, message=nil )
|
15
|
+
@name = name
|
16
|
+
@message = message
|
17
|
+
end
|
18
|
+
|
19
|
+
# :call-seq: station.to_hash -> hash
|
20
|
+
#
|
21
|
+
# Returns a hash representation of the object.
|
22
|
+
# {"name" => "Bank", "message" => "Undergoing escalator refurbishment."}
|
23
|
+
#
|
24
|
+
def to_hash
|
25
|
+
instance_variables.inject( {} ) do |memo, var|
|
26
|
+
memo.merge( {var[1..-1] => instance_variable_get( var )} )
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# :call-seq: station.to_json -> string
|
31
|
+
#
|
32
|
+
# Returns a string of JSON representing the object.
|
33
|
+
# '{"name":"Bank", "message":"Undergoing escalator refurbishment."}'
|
34
|
+
#
|
35
|
+
def to_json( *args )
|
36
|
+
to_hash.to_json( *args )
|
37
|
+
end
|
38
|
+
|
39
|
+
# :call-seq: station.to_xml -> string
|
40
|
+
# station.to_xml(false) -> rexml_document.
|
41
|
+
#
|
42
|
+
# Returns a string of XML representing the object.
|
43
|
+
# <station>
|
44
|
+
# <name>Bank</name>
|
45
|
+
# <message>Undergoing escalator refurbishment.</message>
|
46
|
+
# </station>
|
47
|
+
#
|
48
|
+
# Alternately pass false as the only argument to get an instance of
|
49
|
+
# REXML::Document.
|
50
|
+
#
|
51
|
+
def to_xml( as_string=true )
|
52
|
+
doc = REXML::Document.new
|
53
|
+
root = doc.add_element( "station" )
|
54
|
+
instance_variables.each do |var|
|
55
|
+
root.add_element( var[1..-1] ).add_text( instance_variable_get( var ) )
|
56
|
+
end
|
57
|
+
if as_string then doc.to_s else doc end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Tube # :nodoc:
|
2
|
+
|
3
|
+
# Really just an array that can have a name, plus appropriate #to_json and
|
4
|
+
# #to_xml methods.
|
5
|
+
#
|
6
|
+
class StationGroup < Array
|
7
|
+
attr_reader :name
|
8
|
+
|
9
|
+
# :call-seq: StationGroup.new(name, size=0, obj=nil)
|
10
|
+
# StationGroup.new(name, array)
|
11
|
+
# StationGroup.new(name, size) {|index| block }
|
12
|
+
#
|
13
|
+
# See Array.new.
|
14
|
+
#
|
15
|
+
def initialize( name, *args )
|
16
|
+
@name = name
|
17
|
+
super( *args )
|
18
|
+
end
|
19
|
+
|
20
|
+
# :call-seq: station_group.to_hash -> hash
|
21
|
+
#
|
22
|
+
# Returns a hash representation of the object. Also calls #to_hash on its
|
23
|
+
# contents.
|
24
|
+
# {"Closed stations" => [array contents...]}
|
25
|
+
#
|
26
|
+
def to_hash
|
27
|
+
{@name => to_a.map {|e| e.to_hash}}
|
28
|
+
end
|
29
|
+
|
30
|
+
# :call-seq: station_group.to_json -> string
|
31
|
+
#
|
32
|
+
# Returns a string of JSON representing the object.
|
33
|
+
# '{"Closed stations":[array contents...]}'
|
34
|
+
#
|
35
|
+
def to_json( *args )
|
36
|
+
to_hash.to_json( *args )
|
37
|
+
end
|
38
|
+
|
39
|
+
# :call-seq: station_group.to_xml -> string
|
40
|
+
# station_group.to_xml(false) -> rexml_document.
|
41
|
+
#
|
42
|
+
# Returns a string of XML representing the object.
|
43
|
+
# <station_group>
|
44
|
+
# <name>Closed stations</name>
|
45
|
+
# <stations>
|
46
|
+
# contents of the array as xml...
|
47
|
+
# </stations>
|
48
|
+
# </station_group>
|
49
|
+
#
|
50
|
+
# Alternately pass false as the only argument to get an instance of
|
51
|
+
# REXML::Document.
|
52
|
+
#
|
53
|
+
def to_xml( as_string=true )
|
54
|
+
doc = REXML::Document.new
|
55
|
+
root = doc.add_element( "station_group" )
|
56
|
+
root.add_element( "name" ).add_text( name )
|
57
|
+
stations = root.add_element( "stations" )
|
58
|
+
each do |e|
|
59
|
+
stations.add_element( e.to_xml( false ) )
|
60
|
+
end
|
61
|
+
if as_string then doc.to_s else doc end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Tube # :nodoc:
|
2
|
+
|
3
|
+
# Really just an array with appropriate #to_json and #to_xml methods.
|
4
|
+
#
|
5
|
+
class StationGroupGroup < Array
|
6
|
+
|
7
|
+
# :call-seq: station_group_group.to_hash -> hash
|
8
|
+
#
|
9
|
+
# Returns a hash representation of the object.
|
10
|
+
# Calls #to_hash on its contents (which should be station groups) and then
|
11
|
+
# merges those hashes. The end results should look something like:
|
12
|
+
# {"Closed stations" => [station group contents...],
|
13
|
+
# "Station maintenance" => [station group contents...]}
|
14
|
+
#
|
15
|
+
def to_hash
|
16
|
+
inject( {} ) do |memo, e|
|
17
|
+
memo.merge( e.to_hash )
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# :call-seq: station_group_group.to_json -> string
|
22
|
+
#
|
23
|
+
# Returns a string of JSON representing the object.
|
24
|
+
# '{"Closed stations": [station group contents...],
|
25
|
+
# "Station maintenance": [station group contents...]}'
|
26
|
+
#
|
27
|
+
def to_json( *args )
|
28
|
+
to_hash.to_json( *args )
|
29
|
+
end
|
30
|
+
|
31
|
+
# :call-seq: station_group_group.to_xml -> string
|
32
|
+
# station_group_group.to_xml(false) -> rexml_document.
|
33
|
+
#
|
34
|
+
# Returns a string of XML representing the object.
|
35
|
+
# <station_groups>
|
36
|
+
# <station_group>
|
37
|
+
# <name>Closed stations</name>
|
38
|
+
# <stations>
|
39
|
+
# contents of the station group as xml...
|
40
|
+
# </stations>
|
41
|
+
# </station_group>
|
42
|
+
# <station_group>
|
43
|
+
# <name>Station maintenance</name>
|
44
|
+
# <stations>
|
45
|
+
# contents of the station group as xml...
|
46
|
+
# </stations>
|
47
|
+
# </station_group>
|
48
|
+
# </station_groups>
|
49
|
+
#
|
50
|
+
# Alternately pass false as the only argument to get an instance of
|
51
|
+
# REXML::Document.
|
52
|
+
#
|
53
|
+
def to_xml( as_string=true )
|
54
|
+
doc = REXML::Document.new
|
55
|
+
root = doc.add_element( "station_groups" )
|
56
|
+
each do |e|
|
57
|
+
root.add_element( e.to_xml( false ) )
|
58
|
+
end
|
59
|
+
if as_string then doc.to_s else doc end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/status.rb
ADDED
@@ -0,0 +1,317 @@
|
|
1
|
+
module Tube # :nodoc:
|
2
|
+
|
3
|
+
# Models the status of the London Underground network as displayed on
|
4
|
+
# http://www.tfl.gov.uk/tfl/livetravelnews/realtime/tube/default.html.
|
5
|
+
#
|
6
|
+
# It is a very thin abstraction over the tfl website, as a result the access
|
7
|
+
# to data on stations is somewhat different to lines due to the differing
|
8
|
+
# presentation.
|
9
|
+
# However it is very dynamic, for example should the East London line return
|
10
|
+
# it will show up in the lines array automatically.
|
11
|
+
#
|
12
|
+
# ==Example Usage
|
13
|
+
# require 'tube/status'
|
14
|
+
#
|
15
|
+
# status = Tube::Status.get
|
16
|
+
#
|
17
|
+
# broken_lines = status.lines.select {|line| line.problem?}
|
18
|
+
# broken_lines.collect {|line| line.name}
|
19
|
+
# #=> ["Circle", "District", "Jubilee", "Metropolitan", "Northern"]
|
20
|
+
#
|
21
|
+
# status.line(:circle).message
|
22
|
+
# #=> "Saturday 7 and Sunday 8 March, suspended."
|
23
|
+
#
|
24
|
+
# closed_stations = status.station_group(:closed)
|
25
|
+
# closed_stations.collect {|station| station.name}
|
26
|
+
# #=> ["Blackfriars", "Hatton Cross"]
|
27
|
+
#
|
28
|
+
# status.find_station("hatton").message
|
29
|
+
# #=> "Saturday 7 and Sunday 8 March, closed."
|
30
|
+
#
|
31
|
+
# status.updated.strftime("%I:%M%p")
|
32
|
+
# #=> "04:56PM"
|
33
|
+
#
|
34
|
+
# status.reload
|
35
|
+
# status.updated.strftime("%I:%M%p")
|
36
|
+
# #=> "05:00PM"
|
37
|
+
#
|
38
|
+
# ==Converting to JSON and XML
|
39
|
+
# All objects come complete with #to_json and #to_xml methods. These depend
|
40
|
+
# upon the ruby json and REXML libraries.
|
41
|
+
#
|
42
|
+
# ===XML
|
43
|
+
# status.line(:central).to_xml
|
44
|
+
# #=> "<line><id>central</id><status>Good service</status>
|
45
|
+
# <problem>false</problem><message/><name>Central</name></line>"
|
46
|
+
#
|
47
|
+
# ===JSON
|
48
|
+
# status.line(:central).to_json
|
49
|
+
# #=> '{"id":"central", "status":"Good service", "problem":false,
|
50
|
+
# "message":null, "name":"Central"}'
|
51
|
+
#
|
52
|
+
class Status
|
53
|
+
attr_reader :updated, :lines, :station_groups
|
54
|
+
|
55
|
+
# :call-seq: Status.get -> status
|
56
|
+
# Status.new -> status
|
57
|
+
#
|
58
|
+
# Request and parse the status of the London Underground network from the
|
59
|
+
# tfl.gov.uk "Live travel news" page.
|
60
|
+
#
|
61
|
+
def initialize( url=
|
62
|
+
"http://www.tfl.gov.uk/tfl/livetravelnews/realtime/tube/default.html" )
|
63
|
+
|
64
|
+
@url = url
|
65
|
+
reload
|
66
|
+
end
|
67
|
+
|
68
|
+
class << self
|
69
|
+
alias get new
|
70
|
+
end
|
71
|
+
|
72
|
+
# :call-seq: status.reload -> status
|
73
|
+
#
|
74
|
+
# Re-request the latest status and reload all data.
|
75
|
+
#
|
76
|
+
def reload
|
77
|
+
doc = Hpricot( open( @url ) )
|
78
|
+
|
79
|
+
time_el = doc.at( "div#service-board" ).previous_sibling.children.first
|
80
|
+
time_text = time_el.inner_text.match( /(\d?\d:\d\d(a|p)m)/ )[0]
|
81
|
+
time_zone = if is_bst? then "+0100" else "+0000" end
|
82
|
+
@updated = Time.parse( "#{time_text} #{time_zone}" )
|
83
|
+
|
84
|
+
lines = doc.search( "dl#lines dt" ).map do |el|
|
85
|
+
id = el.attributes["class"]
|
86
|
+
name = el.inner_text.strip
|
87
|
+
|
88
|
+
status = el.next_sibling
|
89
|
+
if status_el = status.at( "h3" )
|
90
|
+
status_text = status_el.inner_text.strip
|
91
|
+
message = format_messages( status.search( "div.message p" ) )
|
92
|
+
else
|
93
|
+
status_text = status.inner_text.strip
|
94
|
+
end
|
95
|
+
problem = status.attributes["class"] == "problem"
|
96
|
+
|
97
|
+
Line.new( id, status_text, problem, message, name )
|
98
|
+
end
|
99
|
+
@lines = LineGroup.new( lines )
|
100
|
+
|
101
|
+
@station_groups = StationGroupGroup.new
|
102
|
+
doc.search( "dl#stations dt" ).each do |el|
|
103
|
+
station_group = StationGroup.new( el.inner_text.strip )
|
104
|
+
while el = el.next_sibling
|
105
|
+
if el.to_html =~ /^<dd/ && name_el = el.at( "h3" )
|
106
|
+
name = name_el.inner_text.strip
|
107
|
+
message = format_messages( el.search( "div.message p" ) )
|
108
|
+
station = Station.new( name, message )
|
109
|
+
station_group.push( station )
|
110
|
+
elsif el.to_html =~ /^<dt/
|
111
|
+
break
|
112
|
+
end
|
113
|
+
end
|
114
|
+
@station_groups.push( station_group )
|
115
|
+
end
|
116
|
+
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
# :call-seq: status.line(string) -> line or nil
|
121
|
+
# status.line(symbol) -> line or nil
|
122
|
+
# status.line(regexp) -> line or nil
|
123
|
+
#
|
124
|
+
# Get a single Line object. Passing a string will do a fuzzy match on the
|
125
|
+
# line id, a symbol will have to match the line id exactly.
|
126
|
+
#
|
127
|
+
# status.line(:hammersmithandcity) #=>\
|
128
|
+
#<Tube::Line:0x1163964 @id="hammersmithandcity", @name="H'smith & City"...
|
129
|
+
# status.line("waterloo") #=>\
|
130
|
+
#<Tube::Line:0x113d700 @id="waterlooandcity", @name="Waterloo & City"...
|
131
|
+
# status.line("east london") #=> nil # no longer exists
|
132
|
+
#
|
133
|
+
def line( name )
|
134
|
+
if name.is_a?( String )
|
135
|
+
name = name.gsub( /\s/, "" )
|
136
|
+
name.gsub!( /&/, "and" )
|
137
|
+
name.gsub!( /'/, ".+" )
|
138
|
+
name = Regexp.new( name, true )
|
139
|
+
elsif name.is_a?( Symbol )
|
140
|
+
name = /^#{name}$/
|
141
|
+
end
|
142
|
+
@lines.detect {|line| line.id =~ name}
|
143
|
+
end
|
144
|
+
|
145
|
+
# :call-seq: status.find_station(string) -> station or nil
|
146
|
+
# status.find_station(symbol) -> station or nil
|
147
|
+
# status.find_station(regexp) -> station or nil
|
148
|
+
#
|
149
|
+
# Get a single Station object. See #find_stations for more details. If more
|
150
|
+
# than one station is found the one with the shortest name will be returned.
|
151
|
+
#
|
152
|
+
def find_station( name )
|
153
|
+
results = find_stations( name )
|
154
|
+
|
155
|
+
if results.length == 1
|
156
|
+
results.first
|
157
|
+
else
|
158
|
+
results.sort do |a,b|
|
159
|
+
a.name.gsub(/\(.+\)/, "").length <=> b.name.gsub(/\(.+\)/, "").length
|
160
|
+
end.first
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# :call-seq: status.find_stations(string) -> array
|
165
|
+
# status.find_stations(symbol) -> array
|
166
|
+
# status.find_stations(regexp) -> array
|
167
|
+
# status.find_stations(array) -> array
|
168
|
+
# status.find_stations -> array
|
169
|
+
#
|
170
|
+
# Get all Station objects matching the argument. Passing a string will do a
|
171
|
+
# fuzzy match on the station name, a symbol will have to match the station
|
172
|
+
# name exactly, an array will return all matches (without duplicates) for
|
173
|
+
# the contents of the array.
|
174
|
+
#
|
175
|
+
# With no arguments simply returns all stations.
|
176
|
+
#
|
177
|
+
# status.find_stations("acton") \
|
178
|
+
#=> [#<Tube::Station:0x110d7e4 @message="Closed.", @name="East Acton">]
|
179
|
+
# status.find_stations(:Bank) #=> [] # No problems at Bank
|
180
|
+
# status.find_stations("tower bridge") #=> [] # No station by that name
|
181
|
+
#
|
182
|
+
def find_stations( name=nil )
|
183
|
+
stations = @station_groups.flatten
|
184
|
+
|
185
|
+
case name
|
186
|
+
when String
|
187
|
+
# this could be simplified once I'm more confident about the format of
|
188
|
+
# station names
|
189
|
+
name = name.gsub( /\s/, "\\s*" )
|
190
|
+
name.gsub!( /(and|&)/, "(and|&)" )
|
191
|
+
name = Regexp.new( name, true )
|
192
|
+
stations = stations.select {|station| station.name =~ name}
|
193
|
+
when Regexp
|
194
|
+
stations = stations.select {|station| station.name =~ name}
|
195
|
+
when Symbol
|
196
|
+
stations = stations.select {|station| station.name == name.to_s}
|
197
|
+
when Array
|
198
|
+
stations = name.map {|n| search_stations( n )}.flatten.uniq
|
199
|
+
when nil
|
200
|
+
stations
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# :call-seq: status.station_group(string) -> station_group or nil
|
205
|
+
# status.station_group(symbol) -> station_group or nil
|
206
|
+
# status.station_group(regexp) -> station_group or nil
|
207
|
+
#
|
208
|
+
# Get a single StationGroup object. Both string and symbol are fuzzy
|
209
|
+
# matches.
|
210
|
+
#
|
211
|
+
# It appears the two groups are "Closed stations" and "Station maintenance"
|
212
|
+
#
|
213
|
+
# status.station_group(:closed) #=> [stations...]
|
214
|
+
# status.station_group(:maintenance) #=> [stations...]
|
215
|
+
#
|
216
|
+
def station_group( name )
|
217
|
+
if name.is_a?( String ) || name.is_a?( Symbol )
|
218
|
+
name = Regexp.new( name.to_s, true )
|
219
|
+
end
|
220
|
+
@station_groups.detect {|group| group.name =~ name}
|
221
|
+
end
|
222
|
+
|
223
|
+
# :call-seq: status.to_hash -> hash
|
224
|
+
#
|
225
|
+
# Returns a hash representation of the object.
|
226
|
+
# {"stations" => {station groups...},
|
227
|
+
# "lines" => [lines...],
|
228
|
+
# "updated" => Fri Feb 13 23:31:30 +0000 2009}
|
229
|
+
#
|
230
|
+
def to_hash
|
231
|
+
{"updated" => updated,
|
232
|
+
"lines" => lines.map {|e| e.to_hash},
|
233
|
+
"stations" => station_groups.to_hash}
|
234
|
+
end
|
235
|
+
|
236
|
+
# :call-seq: status.to_json -> string
|
237
|
+
#
|
238
|
+
# Returns a string of JSON representing the object.
|
239
|
+
# '{"stations":{stations...},
|
240
|
+
# "lines":[lines...],
|
241
|
+
# "updated":"Fri Feb 13 23:31:30 +0000 2009"}'
|
242
|
+
#
|
243
|
+
def to_json( *args )
|
244
|
+
to_hash.to_json( *args )
|
245
|
+
end
|
246
|
+
|
247
|
+
# :call-seq: status.to_xml -> string
|
248
|
+
# status.to_xml(false) -> rexml_document
|
249
|
+
#
|
250
|
+
# Returns a string of XML representing the object.
|
251
|
+
# <status>
|
252
|
+
# <updated>Fri Feb 13 23:31:30 +0000 2009</updated>
|
253
|
+
# lines as xml...
|
254
|
+
# station_groups as xml...
|
255
|
+
# </status>
|
256
|
+
#
|
257
|
+
# Alternately pass false as the only argument to get an instance of
|
258
|
+
# REXML::Document.
|
259
|
+
#
|
260
|
+
def to_xml( as_string=true )
|
261
|
+
doc = REXML::Document.new
|
262
|
+
root = doc.add_element( "status" )
|
263
|
+
root.add_element( "updated" ).add_text( updated.to_s )
|
264
|
+
root.add_element( lines.to_xml( false ) )
|
265
|
+
root.add_element( station_groups.to_xml( false ) )
|
266
|
+
if as_string then doc.to_s else doc end
|
267
|
+
end
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
# :call-seq: format_messages(messages) -> string
|
272
|
+
#
|
273
|
+
# Takes an array of elements, strips those elements of elements they contain
|
274
|
+
# and returns only the text within the root elements.
|
275
|
+
# <p>Suspended. Rail replacement bus services operate.</p>
|
276
|
+
# <p>Service A: ...</p>
|
277
|
+
# <p><a href="/transform">See how we are transforming the Tube</a></p>
|
278
|
+
# becomes
|
279
|
+
# Suspended. Rail replacement bus services operate.
|
280
|
+
# Service A: ...
|
281
|
+
#
|
282
|
+
def format_messages( messages )
|
283
|
+
text_messages = messages.map do |message|
|
284
|
+
if message.children
|
285
|
+
message.children.select {|child| child.text?}.join( " " )
|
286
|
+
end
|
287
|
+
end.compact
|
288
|
+
text_messages.reject! {|m| m.empty?}
|
289
|
+
text_messages.map {|m| m.gsub( /\s+/, " " ).strip}.join( "\n" )
|
290
|
+
end
|
291
|
+
|
292
|
+
# :call-seq: is_bst? -> bool
|
293
|
+
#
|
294
|
+
# Is British Summer Time currently in effect.
|
295
|
+
#
|
296
|
+
def is_bst?
|
297
|
+
bst_start = last_sunday_of_preceding_month( "april" )
|
298
|
+
bst_end = last_sunday_of_preceding_month( "november" )
|
299
|
+
|
300
|
+
one_hour = 3600
|
301
|
+
bst_start = Time.gm(bst_start.year, bst_start.month) + one_hour
|
302
|
+
bst_end = Time.gm(bst_end.year, bst_end.month) + one_hour
|
303
|
+
|
304
|
+
(bst_start..bst_end).include?(Time.now.getgm)
|
305
|
+
end
|
306
|
+
|
307
|
+
# :call-seq: last_sunday_of_preceding_month(month_name) -> date
|
308
|
+
#
|
309
|
+
def last_sunday_of_preceding_month( month_name )
|
310
|
+
start_of_preceding_month = Date.parse( month_name )
|
311
|
+
week_day = start_of_preceding_month.wday
|
312
|
+
distance_from_sunday = if week_day == 0 then 7 else week_day end
|
313
|
+
start_of_preceding_month - distance_from_sunday
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|
317
|
+
end
|
data/lib/tube.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'time'
|
5
|
+
require 'json'
|
6
|
+
require 'rexml/document'
|
7
|
+
|
8
|
+
require 'line'
|
9
|
+
require 'station'
|
10
|
+
require 'line_group'
|
11
|
+
require 'station_group'
|
12
|
+
require 'station_group_group'
|
13
|
+
require 'status'
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: matsadler-tube
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mat Sadler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-24 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hpricot
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.8.1
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: json
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description: A simple Ruby library to access the status of the London Underground network.
|
36
|
+
email: mat@sourcetagsandcodes.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- lib/line.rb
|
45
|
+
- lib/line_group.rb
|
46
|
+
- lib/station.rb
|
47
|
+
- lib/station_group.rb
|
48
|
+
- lib/station_group_group.rb
|
49
|
+
- lib/status.rb
|
50
|
+
- lib/tube.rb
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://github.com/matsadler/tube
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options:
|
55
|
+
- --line-numbers
|
56
|
+
- --inline-source
|
57
|
+
- --title
|
58
|
+
- Tube
|
59
|
+
- --main
|
60
|
+
- Tube::Status
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
version:
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.2.0
|
79
|
+
signing_key:
|
80
|
+
specification_version: 2
|
81
|
+
summary: A simple Ruby library to access the status of the London Underground network.
|
82
|
+
test_files: []
|
83
|
+
|