national-rail 0.3.8 → 0.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/national-rail/journey_planner.rb +54 -14
  2. metadata +85 -6
@@ -5,8 +5,34 @@ require 'tidy_ffi'
5
5
 
6
6
  module NationalRail
7
7
 
8
+ class ParseError < StandardError
9
+ attr_reader :original_exception, :page_html
10
+ def initialize(original_exception, page_html)
11
+ super(original_exception.message)
12
+ @original_exception, @page_html = original_exception, page_html
13
+ end
14
+ def inspect
15
+ "#{self.class} wrapping #{@original_exception.inspect}"
16
+ end
17
+ end
18
+
19
+ class InputError < StandardError; end
20
+
8
21
  class JourneyPlanner
9
22
 
23
+ class << self
24
+ attr_accessor :capture_path
25
+ def capture(page, filename)
26
+ if capture_path.present?
27
+ FileUtils.mkdir_p(capture_path)
28
+ path = File.join(capture_path, filename)
29
+ File.open(path, "w") { |f| f.write(TidyFFI::Tidy.new(page.parser.to_html).clean) }
30
+ end
31
+ rescue => e
32
+ puts e
33
+ end
34
+ end
35
+
10
36
  class NokogiriParser < Mechanize::Page
11
37
  attr_reader :doc
12
38
  def initialize(uri = nil, response = nil, body = nil, code = nil)
@@ -38,23 +64,32 @@ module NationalRail
38
64
 
39
65
  attr_reader :departure_time, :number_of_changes
40
66
 
41
- def initialize(agent, date, departure_time, number_of_changes, link)
67
+ def initialize(agent, date, departure_time, number_of_changes, link, status)
42
68
  @agent, @date = agent, date
43
69
  @departure_time, @number_of_changes, @link = departure_time, number_of_changes, link
70
+ @status = status
71
+ end
72
+
73
+ def cancelled?
74
+ @status =~ %r{cancelled}i
44
75
  end
45
76
 
46
77
  def details
47
- return if number_of_changes.to_i > 0
78
+ return {} if number_of_changes.to_i > 0
79
+ return {} if cancelled?
48
80
  @agent.transact do
49
- parse_details(@link.click, @date)
81
+ details_page = @link.click
82
+ JourneyPlanner.capture(details_page, "details.html")
83
+ parse_details(details_page, @date)
50
84
  end
51
85
  end
52
86
 
53
87
  def parse_details(page, date)
54
88
  details = {}
55
89
  description = (page.doc/"table#journeyLegDetails tbody tr.lastRow td[@colspan='6'] div").last.inner_text.gsub(/\s+/, " ").strip
56
- company = (/(.*) service from .* to .*/.match(description)[1]).strip
57
- details[:company] = company
90
+ company_matches = /(.*) service from .* to .*/.match(description)
91
+ return details unless company_matches
92
+ details[:company] = company_matches[1].strip
58
93
  origins, destinations = (/.* service from (.*) to (.*)/.match(description)[1,2]).map{ |s| s.split(",").map(&:strip) }
59
94
  details[:origins], details[:destinations] = origins, destinations
60
95
  parser = TimeParser.new(date)
@@ -90,9 +125,9 @@ module NationalRail
90
125
  }
91
126
  details
92
127
  rescue => e
93
- filename = File.join(File.dirname(__FILE__), "..", "..", "details-error.html")
94
- File.open(filename, "w") { |f| f.write(page.parser.to_html) }
95
- raise e
128
+ JourneyPlanner.capture(page, "details-error.html")
129
+ page_html = TidyFFI::Tidy.new(page.parser.to_html).clean
130
+ raise ParseError.new(e, page_html)
96
131
  end
97
132
  end
98
133
 
@@ -106,6 +141,7 @@ module NationalRail
106
141
  def plan(options = {})
107
142
  summary_rows = []
108
143
  @agent.get("http://www.nationalrail.co.uk/") do |home_page|
144
+ JourneyPlanner.capture(home_page, "index.html")
109
145
  button = nil
110
146
  times_page = home_page.form_with(:action => "http://ojp.nationalrail.co.uk/en/s/planjourney/plan") do |form|
111
147
  button = form.buttons.last
@@ -132,8 +168,10 @@ module NationalRail
132
168
  form["_includeOvertakenTrains"] = "on"
133
169
  end.click_button(button)
134
170
 
171
+ JourneyPlanner.capture(times_page, "summary.html")
172
+
135
173
  if (times_page.doc/".error-message").any?
136
- raise (times_page.doc/".error-message").first.inner_text.gsub(/\s+/, " ").strip
174
+ raise InputError.new((times_page.doc/".error-message").first.inner_text.gsub(/\s+/, " ").strip)
137
175
  end
138
176
 
139
177
  date = Date.parse((times_page.doc/".journey-details span").first.children.first.inner_text.gsub(/\s+/, " ").gsub(/\+ 1 day/, '').strip)
@@ -145,22 +183,24 @@ module NationalRail
145
183
  next
146
184
  end
147
185
 
148
- departure_time = TimeParser.new(date).time((tr/"td.leaving").inner_text.strip)
186
+ departure_time = TimeParser.new(date).time((tr/"td:nth-child(1)").inner_text.strip)
149
187
  number_of_changes = (tr/"td:nth-child(6)").inner_text.strip
188
+ status = (tr/"td:nth-child(10) .status").inner_text.strip
150
189
 
151
190
  anchor = (tr/"a[@id^=journeyOption]").first
152
191
  next unless anchor
153
192
 
154
193
  link = times_page.links.detect { |l| l.attributes["id"] == anchor.attributes["id"].value }
155
194
 
156
- summary_rows << SummaryRow.new(@agent, date, departure_time, number_of_changes, link)
195
+ summary_rows << SummaryRow.new(@agent, date, departure_time, number_of_changes, link, status)
157
196
  end
158
197
  end
159
198
  summary_rows
160
199
  rescue => e
161
- filename = File.join(File.dirname(__FILE__), "..", "..", "summary-error.html")
162
- File.open(filename, "w") { |f| f.write(TidyFFI::Tidy.new(@agent.current_page.parser.to_html).clean) }
163
- raise e
200
+ page = @agent.current_page
201
+ JourneyPlanner.capture(page, "summary-error.html")
202
+ page_html = TidyFFI::Tidy.new(page.parser.to_html).clean
203
+ raise ParseError.new(e, page_html)
164
204
  end
165
205
  end
166
206
  end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: national-rail
3
3
  version: !ruby/object:Gem::Version
4
+ hash: 7
4
5
  prerelease: false
5
6
  segments:
6
7
  - 0
7
8
  - 3
8
- - 8
9
- version: 0.3.8
9
+ - 10
10
+ version: 0.3.10
10
11
  platform: ruby
11
12
  authors:
12
13
  - James Mead
@@ -14,16 +15,18 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-11-17 00:00:00 +00:00
18
+ date: 2010-12-05 00:00:00 +00:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: mechanize
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ~>
26
28
  - !ruby/object:Gem::Version
29
+ hash: 23
27
30
  segments:
28
31
  - 1
29
32
  - 0
@@ -35,9 +38,11 @@ dependencies:
35
38
  name: hpricot
36
39
  prerelease: false
37
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
38
42
  requirements:
39
43
  - - ~>
40
44
  - !ruby/object:Gem::Version
45
+ hash: 59
41
46
  segments:
42
47
  - 0
43
48
  - 8
@@ -49,9 +54,11 @@ dependencies:
49
54
  name: nokogiri
50
55
  prerelease: false
51
56
  requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
52
58
  requirements:
53
59
  - - ~>
54
60
  - !ruby/object:Gem::Version
61
+ hash: 15
55
62
  segments:
56
63
  - 1
57
64
  - 4
@@ -63,9 +70,11 @@ dependencies:
63
70
  name: activesupport
64
71
  prerelease: false
65
72
  requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
66
74
  requirements:
67
75
  - - ~>
68
76
  - !ruby/object:Gem::Version
77
+ hash: 7
69
78
  segments:
70
79
  - 3
71
80
  - 0
@@ -74,19 +83,85 @@ dependencies:
74
83
  type: :runtime
75
84
  version_requirements: *id004
76
85
  - !ruby/object:Gem::Dependency
77
- name: tidy_ffi
86
+ name: ffi
78
87
  prerelease: false
79
88
  requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - "="
92
+ - !ruby/object:Gem::Version
93
+ hash: 1
94
+ segments:
95
+ - 0
96
+ - 6
97
+ - 3
98
+ version: 0.6.3
99
+ type: :runtime
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: tidy_ffi
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
80
106
  requirements:
81
107
  - - ~>
82
108
  - !ruby/object:Gem::Version
109
+ hash: 29
83
110
  segments:
84
111
  - 0
85
112
  - 1
86
113
  - 3
87
114
  version: 0.1.3
88
115
  type: :runtime
89
- version_requirements: *id005
116
+ version_requirements: *id006
117
+ - !ruby/object:Gem::Dependency
118
+ name: i18n
119
+ prerelease: false
120
+ requirement: &id007 !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ hash: 13
126
+ segments:
127
+ - 0
128
+ - 4
129
+ - 1
130
+ version: 0.4.1
131
+ type: :runtime
132
+ version_requirements: *id007
133
+ - !ruby/object:Gem::Dependency
134
+ name: tzinfo
135
+ prerelease: false
136
+ requirement: &id008 !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ hash: 61
142
+ segments:
143
+ - 0
144
+ - 3
145
+ - 23
146
+ version: 0.3.23
147
+ type: :runtime
148
+ version_requirements: *id008
149
+ - !ruby/object:Gem::Dependency
150
+ name: webmock
151
+ prerelease: false
152
+ requirement: &id009 !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ hash: 13
158
+ segments:
159
+ - 1
160
+ - 6
161
+ - 1
162
+ version: 1.6.1
163
+ type: :development
164
+ version_requirements: *id009
90
165
  description:
91
166
  email: james@floehopper.org
92
167
  executables: []
@@ -113,23 +188,27 @@ rdoc_options: []
113
188
  require_paths:
114
189
  - lib
115
190
  required_ruby_version: !ruby/object:Gem::Requirement
191
+ none: false
116
192
  requirements:
117
193
  - - ">="
118
194
  - !ruby/object:Gem::Version
195
+ hash: 3
119
196
  segments:
120
197
  - 0
121
198
  version: "0"
122
199
  required_rubygems_version: !ruby/object:Gem::Requirement
200
+ none: false
123
201
  requirements:
124
202
  - - ">="
125
203
  - !ruby/object:Gem::Version
204
+ hash: 3
126
205
  segments:
127
206
  - 0
128
207
  version: "0"
129
208
  requirements: []
130
209
 
131
210
  rubyforge_project: national-rail
132
- rubygems_version: 1.3.6
211
+ rubygems_version: 1.3.7
133
212
  signing_key:
134
213
  specification_version: 3
135
214
  summary: A Ruby API for the National Rail website