matsadler-tube 0.1.1 → 0.1.2

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 (4) hide show
  1. data/lib/status.rb +16 -76
  2. data/lib/status_parser.rb +130 -0
  3. data/lib/tube.rb +2 -0
  4. metadata +5 -3
@@ -76,44 +76,31 @@ module Tube # :nodoc:
76
76
  def reload
77
77
  doc = Hpricot( open( @url ) )
78
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}" )
79
+ results = Tube::StatusParser.parse( doc )
83
80
 
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"
81
+ @updated = results[:updated]
82
+
83
+ lines = results[:lines].map do |line|
84
+ id = line[:html_class]
85
+ name = line[:name]
86
+ status_text = line[:status][:headline]
87
+ problem = line[:status][:problem]
88
+ message = line[:status][:message]
96
89
 
97
90
  Line.new( id, status_text, problem, message, name )
98
91
  end
99
92
  @lines = LineGroup.new( lines )
100
93
 
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
94
+ station_groups = results[:station_groups].map do |station_group|
95
+ stations = station_group[:stations].map do |station|
96
+ Station.new( station[:name], station[:message] )
113
97
  end
114
- @station_groups.push( station_group )
98
+
99
+ StationGroup.new( station_group[:name], stations )
115
100
  end
116
101
 
102
+ @station_groups = StationGroupGroup.new( station_groups )
103
+
117
104
  self
118
105
  end
119
106
 
@@ -266,52 +253,5 @@ module Tube # :nodoc:
266
253
  if as_string then doc.to_s else doc end
267
254
  end
268
255
 
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
256
  end
317
257
  end
@@ -0,0 +1,130 @@
1
+ module Tube # :nodoc:
2
+ module StatusParser
3
+ extend self
4
+
5
+ def parse( hpricot_doc )
6
+ service_board = hpricot_doc.at( "#service-board" )
7
+
8
+ updated_element = service_board.previous_sibling.children.first
9
+ updated = parse_updated( updated_element )
10
+
11
+ lines = service_board.search( "dl#lines dt" ).map do |line_element|
12
+ parse_line( line_element )
13
+ end
14
+
15
+ station_group_elements = service_board.search( "dl#stations dt" )
16
+ station_groups = station_group_elements.map do |station_group_element|
17
+ parse_station_group( station_group_element )
18
+ end
19
+
20
+ {:updated => updated, :lines => lines, :station_groups => station_groups}
21
+ end
22
+
23
+ def parse_updated( updated_element )
24
+ time_text = updated_element.inner_text.match( /(\d?\d:\d\d(a|p)m)/ )[0]
25
+ time_zone = if is_bst? then "+0100" else "+0000" end
26
+
27
+ Time.parse( "#{time_text} #{time_zone}" )
28
+ end
29
+
30
+ def parse_line( line_element )
31
+ name = line_element.inner_text.strip
32
+ html_class = line_element.attributes["class"]
33
+ status = parse_status( line_element.next_sibling )
34
+
35
+ {:name => name, :html_class => html_class, :status => status}
36
+ end
37
+
38
+ def parse_status( status_element )
39
+ header = status_element.at( "h3" )
40
+
41
+ if header
42
+ headline = header.inner_text.strip
43
+ message = parse_status_message( status_element.search( "div.message p" ) )
44
+ else
45
+ headline = status_element.inner_text.strip
46
+ end
47
+ problem = status_element.attributes["class"] == "problem"
48
+
49
+ {:headline => headline, :problem => problem, :message => message}
50
+ end
51
+
52
+ def parse_station_group( station_group_element )
53
+ name = station_group_element.inner_text.strip
54
+ stations = []
55
+
56
+ station_element = station_group_element
57
+ while station_element = station_element.next_sibling
58
+ if station_element.to_html =~ /^<dd/
59
+ stations.push( parse_station( station_element ) )
60
+ elsif station_element.to_html =~ /^<dt/
61
+ break
62
+ end
63
+ end
64
+
65
+ {:name => name, :stations => stations}
66
+ end
67
+
68
+ def parse_station( station_element )
69
+ name = station_element.at( "h3" ).inner_text.strip
70
+ message = parse_status_message( station_element.search( "div.message p" ) )
71
+
72
+ {:name => name, :message => message}
73
+ end
74
+
75
+ def parse_status_message( messages )
76
+ text_messages = messages.map do |message|
77
+ if message.children
78
+ message.children.select {|child| child.text?}.join( " " )
79
+ end
80
+ end.compact
81
+ text_messages.reject! {|m| m.empty?}
82
+
83
+ text_messages.map {|m| m.gsub( /\s+/, " " ).strip}.join( "\n" )
84
+ end
85
+
86
+ private
87
+
88
+ # :call-seq: is_bst? -> bool
89
+ #
90
+ # Is British Summer Time currently in effect.
91
+ #
92
+ def is_bst?
93
+ bst_start = last_sunday_of_month( "march" )
94
+ bst_end = last_sunday_of_month( "october" )
95
+
96
+ one_hour = 3600
97
+ bst_start = Time.gm( bst_start.year, bst_start.month ) + one_hour
98
+ bst_end = Time.gm( bst_end.year, bst_end.month ) + one_hour
99
+
100
+ (bst_start..bst_end).include?( Time.now.getgm )
101
+ end
102
+
103
+ # :call-seq: last_sunday_of_month(month_name) -> date
104
+ #
105
+ def last_sunday_of_month( month )
106
+ start_of_next_month = Date.parse( next_month_name( month ) )
107
+
108
+ week_day = start_of_next_month.wday
109
+
110
+ distance_from_sunday = if week_day == 0 then 7 else week_day end
111
+ start_of_next_month - distance_from_sunday
112
+ end
113
+
114
+ # :call-seq: next_month_name(month_name) -> string
115
+ #
116
+ def next_month_name( month )
117
+ index = Date::MONTHNAMES.index( month.capitalize )
118
+ index ||= ABBR_MONTHNAMES.index( month.capitalize )
119
+
120
+ index += 1
121
+
122
+ if index >= 12
123
+ index = 1
124
+ end
125
+
126
+ Date::MONTHNAMES[index]
127
+ end
128
+
129
+ end
130
+ end
@@ -2,9 +2,11 @@ require 'rubygems'
2
2
  require 'hpricot'
3
3
  require 'open-uri'
4
4
  require 'time'
5
+ require 'date'
5
6
  require 'json'
6
7
  require 'rexml/document'
7
8
 
9
+ require 'status_parser'
8
10
  require 'line'
9
11
  require 'station'
10
12
  require 'line_group'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matsadler-tube
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mat Sadler
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-25 00:00:00 -07:00
12
+ date: 2009-08-01 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -42,6 +42,7 @@ extra_rdoc_files: []
42
42
 
43
43
  files:
44
44
  - lib/line.rb
45
+ - lib/status_parser.rb
45
46
  - lib/line_group.rb
46
47
  - lib/station.rb
47
48
  - lib/station_group.rb
@@ -50,6 +51,7 @@ files:
50
51
  - lib/tube.rb
51
52
  has_rdoc: true
52
53
  homepage: http://github.com/matsadler/tube
54
+ licenses:
53
55
  post_install_message:
54
56
  rdoc_options:
55
57
  - --line-numbers
@@ -75,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
77
  requirements: []
76
78
 
77
79
  rubyforge_project:
78
- rubygems_version: 1.2.0
80
+ rubygems_version: 1.3.5
79
81
  signing_key:
80
82
  specification_version: 2
81
83
  summary: A simple Ruby library to access the status of the London Underground network.