tube 0.2.1 → 0.2.3

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.
@@ -4,7 +4,7 @@ A simple MIT licensed Ruby library to access the status of the London Undergroun
4
4
 
5
5
  == Installation
6
6
 
7
- $ gem install tube --source http://gemcutter.org
7
+ $ gem install tube
8
8
 
9
9
  == Examples
10
10
  require 'tube/status'
@@ -0,0 +1,37 @@
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, :yellow_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
+ }
22
+ STATUS_COLOR = [:blue, :bright_white_bg]
23
+ PROBLEM_COLOR = STATUS_COLOR + [:negative]
24
+
25
+ status = Tube::Status.new
26
+
27
+ longest_name = status.lines.map {|l| l.name.length}.max
28
+ longest_status = status.lines.map {|l| l.status.length}.max
29
+ formatted_time = status.updated.strftime("%I:%M%p").downcase.sub(/^0/, "")
30
+
31
+ puts " Live travel news".style(:bold), " Last update: #{formatted_time}", ""
32
+ status.lines.each do |line|
33
+ print " "
34
+ printf(" %-#{longest_name}s ".color(*LINE_COLORS[line.id]), line.name)
35
+ status_color = if line.problem? then PROBLEM_COLOR else STATUS_COLOR end
36
+ printf(" %-#{longest_status}s \n".color(*status_color), line.status)
37
+ end
@@ -1,79 +1,72 @@
1
1
  require 'time'
2
2
  require 'date'
3
3
  require 'rubygems'
4
- require 'hpricot'
4
+ require 'nokogiri'
5
5
 
6
6
  module Tube # :nodoc:
7
7
  module StatusParser # :nodoc:
8
8
  extend self
9
9
 
10
10
  def parse( html_doc )
11
- html_doc.gsub!(" ", " ")
12
- service_board = Hpricot( html_doc ).at( "#service-board" )
11
+ html_doc.gsub!( / /, " " )
12
+ service_board = Nokogiri::HTML( html_doc ).at_css( "#service-board" )
13
13
 
14
- updated_element = service_board.previous_sibling.children.first
14
+ updated_element = service_board.previous_element.at_css( "h2" )
15
15
  updated = parse_updated( updated_element )
16
16
 
17
- lines = service_board.search( "dl#lines dt" ).map do |line_element|
18
- parse_line( line_element )
19
- end
17
+ line_elements = service_board.css( "ul#lines > li.ltn-line" )
18
+ lines = line_elements.map( &method( :parse_line ) )
20
19
 
21
- station_group_elements = service_board.search( "dl#stations dt" )
22
- station_groups = station_group_elements.map do |station_group_element|
23
- parse_station_group( station_group_element )
24
- end
20
+ station_group_elements = service_board.css( "ul#stations > li" )
21
+ station_groups = station_group_elements.map(&method(:parse_station_group))
25
22
 
26
23
  {:updated => updated, :lines => lines, :station_groups => station_groups}
27
24
  end
28
25
 
29
26
  def parse_updated( updated_element )
30
- time_text = updated_element.inner_text.match( /(\d?\d:\d\d)/ )[0]
27
+ time_text = updated_element.content.match( /(\d?\d:\d\d)/ )[0]
31
28
  time_zone = if is_bst? then "+0100" else "+0000" end
32
29
 
33
30
  Time.parse( "#{time_text} #{time_zone}" )
34
31
  end
35
32
 
36
33
  def parse_line( line_element )
37
- name = line_element.inner_text.strip
38
- html_class = line_element.attributes["class"]
39
- status = parse_status( line_element.next_sibling )
34
+ name = line_element.at_css( "h3.ltn-name" ).content
35
+ html_class = line_element["class"].split.first
36
+ status = parse_status( line_element.at_css( "div.status" ) )
40
37
 
41
38
  {:name => name, :html_class => html_class, :status => status}
42
39
  end
43
40
 
44
41
  def parse_status( status_element )
45
- header = status_element.at( "h3" )
42
+ header = status_element.at_css( "h4.ltn-title" )
46
43
 
47
44
  if header
48
- headline = header.inner_text.strip
49
- message = parse_status_message( status_element.search( "div.message p" ) )
45
+ headline = header.content.strip
46
+ message = parse_status_message( status_element.css("div.message > p") )
50
47
  else
51
- headline = status_element.inner_text.strip
48
+ headline = status_element.content.strip
52
49
  end
53
- problem = status_element.attributes["class"] == "problem"
50
+ problem = status_element["class"].split.include?( "problem" )
54
51
 
55
52
  {:headline => headline, :problem => problem, :message => message}
56
53
  end
57
54
 
58
55
  def parse_station_group( station_group_element )
59
- name = station_group_element.inner_text.strip
60
- stations = []
61
-
62
- station_element = station_group_element
63
- while station_element = station_element.next_sibling
64
- if station_element.to_html =~ /^<dd/
65
- stations.push( parse_station( station_element ) )
66
- elsif station_element.to_html =~ /^<dt/
67
- break
68
- end
56
+ name = station_group_element.at_css( "h3" ).content
57
+
58
+ station_elements = station_group_element.css( "ul > li.ltn-station" )
59
+
60
+ stations = station_elements.map do |station_element|
61
+ parse_station( station_element )
69
62
  end
70
63
 
71
64
  {:name => name, :stations => stations}
72
65
  end
73
66
 
74
67
  def parse_station( station_element )
75
- name = station_element.at( "h3" ).inner_text.strip
76
- message = parse_status_message( station_element.search("div.message p") )
68
+ name = station_element.at_css( "h4.ltn-name" ).content.strip
69
+ message = parse_status_message( station_element.css( "div.message > p" ) )
77
70
 
78
71
  {:name => name, :message => message}
79
72
  end
data/test/dummy.html CHANGED
@@ -14,56 +14,125 @@
14
14
  <div id="main-content">
15
15
  <h1>Live travel news</h1>
16
16
 
17
- <h2>Service update at 12:12 <a href="later.html" id="works_later" class="alert" name="works_later">View engineering works planned for <strong>later today</strong></a></h2>
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>
18
34
  <div id="service-board">
19
- <dl id="lines">
20
- <dt class="central">Central</dt>
21
- <dd>Good service</dd>
22
- <dt class="district">District</dt>
23
- <dd class="problem">
24
- <h3>Part closure</h3>
25
- <div class="message">
26
- <p>Rail replacement bus</p>
27
- <p>Service A: details...</p>
28
- <p><a href="/transform">See how we are transforming the Tube</a></p>
29
- </div>
30
- </dd>
31
- <dt class="waterlooandcity">Waterloo &amp; City</dt>
32
- <dd class="problem">
33
- <h3>Planned closure</h3>
34
- <div class="message">
35
- <p>Closed Sunday.</p>
36
- <p><a href="/transform">See how we are transforming the Tube</a></p>
37
- </div>
38
- </dd>
39
- </dl>
40
- <dl id="stations">
41
- <dt>Closed stations</dt>
42
- <dd>
43
- <h3>Bank</h3>
44
- <div class="message">
45
- <p>Closed due to excessive noise.</p>
46
- <p><a href="/transform">See how we are transforming the Tube</a></p>
47
- </div>
48
- </dd>
49
- <dd>
50
- <h3>Holborn</h3>
51
- <div class="message">
52
- <p>Closed due to fire investigation.</p>
53
- <p>Go to Chancery Lane or Tottenham Court Road instead.</p>
54
- <p><a href="/transform">See how we are transforming the Tube</a></p>
35
+ <h2>Tube lines</h2>
36
+ <ul id="lines">
37
+ <li class="central ltn-line">
38
+ <h3 class="central ltn-name">Central</h3>
39
+ <div class="status">Good service</div>
40
+ </li>
41
+
42
+ <li class="district ltn-line">
43
+ <h3 class="district ltn-name">District</h3>
44
+ <div class="problem status">
45
+ <h4 class="ltn-title">Part closure</h4>
46
+ <div class="message">
47
+ <p>Rail replacement bus</p>
48
+ <p>Service A: details...</p>
49
+ <dl class="linklist">
50
+ <dd>
51
+ <a href="http://journeyplanner.tfl.gov.uk/">Replan your journey</a>
52
+ </dd>
53
+ </dl>
54
+ </div>
55
55
  </div>
56
- </dd>
56
+ </li>
57
57
 
58
- <dt>Station maintenance</dt>
59
- <dd>
60
- <h3>Elephant &amp; Castle</h3>
61
- <div class="message">
62
- <p>Reduced lift service.</p>
63
- <p><a href="/transform">See how we are transforming the Tube</a></p>
58
+ <li class="waterlooandcity ltn-line">
59
+ <h3 class="waterlooandcity ltn-name">Waterloo &amp; City</h3>
60
+ <div class="problem status">
61
+ <h4 class="ltn-title">Planned closure</h4>
62
+ <div class="message">
63
+ <p>Closed Sunday.</p>
64
+ <dl class="linklist">
65
+ <dd>
66
+ <a href="http://journeyplanner.tfl.gov.uk/">Replan your journey</a>
67
+ </dd>
68
+ </dl>
69
+ </div>
64
70
  </div>
65
- </dd>
66
- </dl>
71
+ </li>
72
+ </ul>
73
+
74
+ <h2>Tube stations</h2>
75
+ <ul id="stations">
76
+ <li class="ltn-closed">
77
+ <h3>Closed stations</h3>
78
+ <ul>
79
+ <li class="ltn-station" id="stop-10000xx">
80
+ <h4 class="ltn-name">Bank</h4>
81
+ <div class="message">
82
+ <p>Closed due to excessive noise.</p>
83
+ <dl class="linklist">
84
+ <dd>
85
+ Replan your journey
86
+ <a href="journeyplanner">to</a>
87
+ /
88
+ <a href="http://journeyplanner">from</a>
89
+ this station
90
+ </dd>
91
+ </dl>
92
+ </div>
93
+ </li>
94
+
95
+ <li class="ltn-station" id="stop-10000xx">
96
+ <h4 class="ltn-name">Holborn</h4>
97
+ <div class="message">
98
+ <p>Closed due to fire investigation.</p>
99
+ <p>Go to Chancery Lane or Tottenham Court Road instead.</p>
100
+ <dl class="linklist">
101
+ <dd>
102
+ Replan your journey
103
+ <a href="journeyplanner">to</a>
104
+ /
105
+ <a href="http://journeyplanner">from</a>
106
+ this station
107
+ </dd>
108
+ </dl>
109
+ </div>
110
+ </li>
111
+ </ul>
112
+ </li>
113
+
114
+ <li>
115
+ <h3>Station maintenance</h3>
116
+ <p class="ltn-more-info">(lifts, escalators etc.)</p>
117
+ <ul>
118
+ <li class="ltn-station" id="stop-10000xx">
119
+ <h4 class="ltn-name">Elephant &amp; Castle</h4>
120
+ <div class="message">
121
+ <p>Reduced lift service.</p>
122
+ <dl class="linklist">
123
+ <dd>
124
+ Replan your journey
125
+ <a href="journeyplanner">to</a>
126
+ /
127
+ <a href="http://journeyplanner">from</a>
128
+ this station
129
+ </dd>
130
+ </dl>
131
+ </div>
132
+ </li>
133
+ </ul>
134
+ </li>
135
+ </ul>
67
136
  </div>
68
137
  </div>
69
138
  </div>
@@ -4,24 +4,24 @@ require "#{File.dirname( __FILE__ )}/../lib/tube/status"
4
4
 
5
5
  class TestStatusParser < Test::Unit::TestCase
6
6
  def test_parse_updated
7
- document = Hpricot("<h2>Service update at 18:26</h2>")
8
- element = document.at("h2")
7
+ document = Nokogiri::HTML("<h2>Service update at 18:26</h2>")
8
+ element = document.at_css("h2")
9
9
  result = Tube::StatusParser.parse_updated(element)
10
10
 
11
11
  assert_equal(Time.parse("6:26pm"), result)
12
12
  end
13
13
 
14
14
  def test_parse_updated_with_anchor_in_element
15
- document = Hpricot(%Q{<h2>Service update at 12:12 <a href="/later.html">View engineering works planned for later today</a></h2>})
16
- element = document.at("h2")
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
17
  result = Tube::StatusParser.parse_updated(element)
18
18
 
19
19
  assert_equal(Time.parse("12:12pm"), result)
20
20
  end
21
21
 
22
22
  def test_parse_line
23
- document = Hpricot(%Q{<dt class="central">Central</dt> <dd>Good service</dd>})
24
- element = document.at("dt")
23
+ document = Nokogiri::HTML(%Q{<li class="central ltn-line"><h3 class="central ltn-name">Central</h3><div class="status">Good service</div></li>})
24
+ element = document.at_css("li")
25
25
  result = Tube::StatusParser.parse_line(element)
26
26
 
27
27
  assert_equal("Central", result[:name])
@@ -30,8 +30,8 @@ class TestStatusParser < Test::Unit::TestCase
30
30
  end
31
31
 
32
32
  def test_parse_line_with_complex_name
33
- document = Hpricot(%Q{<dt class="waterlooandcity">Waterloo &amp; City</dt> <dd></dd>})
34
- element = document.at("dt")
33
+ document = Nokogiri::HTML(%Q{<li class="waterlooandcity ltn-line"><h3 class="waterlooandcity ltn-name">Waterloo &amp; City</h3><div class="status">Good service</div></li>})
34
+ element = document.at_css("li")
35
35
  result = Tube::StatusParser.parse_line(element)
36
36
 
37
37
  assert_equal("Waterloo & City", result[:name])
@@ -39,32 +39,32 @@ class TestStatusParser < Test::Unit::TestCase
39
39
  end
40
40
 
41
41
  def test_parse_status
42
- document = Hpricot("<dd>Good service</dd>")
43
- element = document.at("dd")
42
+ document = Nokogiri::HTML(%Q{<div class="status">Good service</div>})
43
+ element = document.at_css("div")
44
44
  result = Tube::StatusParser.parse_status(element)
45
45
 
46
46
  assert_equal("Good service", result[:headline])
47
47
  end
48
48
 
49
49
  def test_parse_status_with_problem
50
- document = Hpricot(%Q{<dd class="problem">Part suspended</dd>})
51
- element = document.at("dd")
50
+ document = Nokogiri::HTML(%Q{<div class="problem status">Part suspended</div>})
51
+ element = document.at_css("div")
52
52
  result = Tube::StatusParser.parse_status(element)
53
53
 
54
54
  assert_equal(true, result[:problem])
55
55
  end
56
56
 
57
57
  def test_parse_status_with_header
58
- document = Hpricot(%Q{<dd><h3>Part suspended</h3></dd>})
59
- element = document.at("dd")
58
+ document = Nokogiri::HTML(%Q{<div class="status"><h4 class="ltn-title">Part suspended</h4></div>})
59
+ element = document.at_css("div")
60
60
  result = Tube::StatusParser.parse_status(element)
61
61
 
62
62
  assert_equal("Part suspended", result[:headline])
63
63
  end
64
64
 
65
65
  def test_parse_status_with_message
66
- document = Hpricot(%Q{<dd class="problem"><h3>Part closure</h3><div class="message"><p>engineering works, etc...</p></div></dd>})
67
- element = document.at("dd")
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
68
  result = Tube::StatusParser.parse_status(element)
69
69
 
70
70
  assert_equal("Part closure", result[:headline])
@@ -73,34 +73,34 @@ class TestStatusParser < Test::Unit::TestCase
73
73
  end
74
74
 
75
75
  def test_parse_status_message
76
- document = Hpricot(%Q{<div><p>engineering works, etc...</p></div>})
77
- elements = document.search("div p")
76
+ document = Nokogiri::HTML(%Q{<div><p>engineering works, etc...</p></div>})
77
+ elements = document.css("div p")
78
78
  result = Tube::StatusParser.parse_status_message(elements)
79
79
 
80
80
  assert_equal("engineering works, etc...", result)
81
81
  end
82
82
 
83
83
  def test_parse_status_message_with_multi_paragraph_message
84
- document = Hpricot(%Q{<div><p>Rail replacement bus</p><p>Service A: details...</p></div>})
85
- elements = document.search("div p")
84
+ document = Nokogiri::HTML(%Q{<div><p>Rail replacement bus</p><p>Service A: details...</p></div>})
85
+ elements = document.css("div p")
86
86
  result = Tube::StatusParser.parse_status_message(elements)
87
87
 
88
88
  assert_equal("Rail replacement bus\nService A: details...", result)
89
89
  end
90
90
 
91
91
  def test_parse_status_message_removes_anchor_from_message
92
- document = Hpricot(%Q{<div><p>Closed Sunday.</p><p><a href="/transform">See how we are transforming the Tube</a></p></div>})
93
- elements = document.search("div p")
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
94
  result = Tube::StatusParser.parse_status_message(elements)
95
95
 
96
96
  assert_equal("Closed Sunday.", result)
97
97
  end
98
98
 
99
99
  def test_parse_station_group
100
- document = Hpricot(%Q{<dt>Closed stations</dt>
101
- <dd><h3>Bank</h3><div class="message"><p>Closed due to excessive noise.</p></div></dd>
102
- <dd><h3>Holborn</h3><div class="message"><p>Closed due to fire investigation.</p></div></dd>})
103
- element = document.at("dt")
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
104
  result = Tube::StatusParser.parse_station_group(element)
105
105
 
106
106
  assert_equal("Closed stations", result[:name])
@@ -109,8 +109,8 @@ class TestStatusParser < Test::Unit::TestCase
109
109
  end
110
110
 
111
111
  def test_parse_station
112
- document = Hpricot(%Q{<dd><h3>Bank</h3><div class="message"><p>Closed due to excessive noise.</p></div></dd>})
113
- element = document.at("dd")
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
114
  result = Tube::StatusParser.parse_station(element)
115
115
 
116
116
  assert_equal("Bank", result[:name])
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tube
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 3
9
+ version: 0.2.3
5
10
  platform: ruby
6
11
  authors:
7
12
  - Matthew Sadler
@@ -9,19 +14,23 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-03-09 00:00:00 +00:00
17
+ date: 2010-04-14 00:00:00 +01:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
- name: hpricot
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
21
+ name: nokogiri
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
23
- version: 0.8.1
24
- version:
27
+ segments:
28
+ - 1
29
+ - 4
30
+ - 1
31
+ version: 1.4.1
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  description: A simple Ruby library to access the status of the London Underground network.
26
35
  email: mat@sourcetagsandcodes.com
27
36
  executables: []
@@ -29,7 +38,7 @@ executables: []
29
38
  extensions: []
30
39
 
31
40
  extra_rdoc_files:
32
- - README.txt
41
+ - README.rdoc
33
42
  files:
34
43
  - lib/tube/line.rb
35
44
  - lib/tube/station.rb
@@ -37,7 +46,8 @@ files:
37
46
  - lib/tube/status_parser.rb
38
47
  - test/dummy.html
39
48
  - test/status_parser_test.rb
40
- - README.txt
49
+ - examples/service_board.rb
50
+ - README.rdoc
41
51
  has_rdoc: true
42
52
  homepage: http://github.com/matsadler/tube
43
53
  licenses: []
@@ -45,25 +55,27 @@ licenses: []
45
55
  post_install_message:
46
56
  rdoc_options:
47
57
  - --main
48
- - README.txt
58
+ - README.rdoc
49
59
  require_paths:
50
60
  - lib
51
61
  required_ruby_version: !ruby/object:Gem::Requirement
52
62
  requirements:
53
63
  - - ">="
54
64
  - !ruby/object:Gem::Version
65
+ segments:
66
+ - 0
55
67
  version: "0"
56
- version:
57
68
  required_rubygems_version: !ruby/object:Gem::Requirement
58
69
  requirements:
59
70
  - - ">="
60
71
  - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
61
74
  version: "0"
62
- version:
63
75
  requirements: []
64
76
 
65
77
  rubyforge_project:
66
- rubygems_version: 1.3.5
78
+ rubygems_version: 1.3.6
67
79
  signing_key:
68
80
  specification_version: 3
69
81
  summary: Access the status of the London Underground network.