amtrak 1.2.2 → 2.0.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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/amtrak.gemspec +2 -1
- data/lib/amtrak.rb +6 -0
- data/lib/amtrak/train_fetcher.rb +21 -7
- data/lib/amtrak/train_fetcher/main_page.rb +28 -25
- data/lib/amtrak/train_fetcher/train_page.rb +17 -14
- data/lib/amtrak/version.rb +1 -1
- data/spec/amtrak/train_fetcher/main_page_spec.rb +5 -27
- data/spec/amtrak/train_fetcher/train_page_spec.rb +2 -21
- data/spec/amtrak/train_fetcher_spec.rb +3 -3
- data/spec/amtrak_spec.rb +2 -1
- data/spec/fixtures/json/_get.json +54 -1
- data/spec/fixtures/vcr/Amtrak/_get/returns_a_list_of_train_times.yml +1020 -1127
- data/spec/fixtures/vcr/Amtrak_TrainFetcher/_get/does_the_same_as_get.yml +1376 -930
- data/spec/fixtures/vcr/Amtrak_TrainFetcher/_get/given_a_valid_date_and_invalid_train_stations/does_not_include_various_classes_and_includes_an_error.yml +875 -659
- data/spec/fixtures/vcr/Amtrak_TrainFetcher/_get/given_a_valid_date_and_train_stations/includes_various_classes.yml +1376 -925
- data/spec/fixtures/vcr/Amtrak_TrainFetcher/_get/given_an_invalid_date_and_valid_train_stations/does_not_include_various_classes_and_includes_an_error.yml +878 -663
- data/spec/fixtures/vcr/Amtrak_TrainFetcher_MainPage/_session_id/pulls_the_session_id_from_the_cookies.yml +905 -845
- data/spec/fixtures/vcr/Amtrak_TrainFetcher_MainPage/_total_pages/when_more_than_one_page_exists_on_the_website/returns_2.yml +1033 -781
- data/spec/fixtures/vcr/Amtrak_TrainFetcher_MainPage/_total_pages/when_only_one_page_exists_on_the_website/returns_1.yml +936 -790
- data/spec/support/vcr.rb +2 -1
- metadata +22 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6b65ee543c0ab3f6c50c3f6af2d873fdb6ea8bf
|
4
|
+
data.tar.gz: 0bbe04375b423a44476f15dceaa7f85a293756fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 458227e5403222869fe87f704ee5ea19aeb61c936299fd5a4684979531ec473240fb5bec3df3bbd6bf7de963b2d0b7483d360cb0bdefed152ef333c49fe01a0e
|
7
|
+
data.tar.gz: 7e3ca8eea40bda322a3f5a7edefbf090e5a50cd6933726eb81a2919da430fc39b860fb276d3e7b8036f984c9715120f54482649c83e4edad33ab743e530840da
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2.2
|
data/amtrak.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ['lib']
|
19
19
|
|
20
|
-
spec.add_dependency '
|
20
|
+
spec.add_dependency 'mechanize', '~> 2.7'
|
21
21
|
spec.add_dependency 'nokogiri', '~> 1.6.4'
|
22
22
|
|
23
23
|
spec.add_development_dependency 'rake'
|
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency 'rubocop', '~> 0.25.0'
|
27
27
|
spec.add_development_dependency 'simplecov', '~> 0.8.2'
|
28
28
|
spec.add_development_dependency 'vcr', '~> 2.9.2'
|
29
|
+
spec.add_development_dependency 'webmock', '~> 1.24'
|
29
30
|
end
|
data/lib/amtrak.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
1
3
|
# Main Amtrak module
|
2
4
|
module Amtrak
|
3
5
|
class Error < StandardError; end
|
4
6
|
|
7
|
+
def self.logger
|
8
|
+
@logger ||= Logger.new(STDOUT)
|
9
|
+
end
|
10
|
+
|
5
11
|
def self.get(from, to, date: nil)
|
6
12
|
Amtrak::TrainFetcher.get(from, to, date: date).map do |html|
|
7
13
|
Amtrak::TrainParser.parse(html)
|
data/lib/amtrak/train_fetcher.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
|
1
3
|
module Amtrak
|
2
4
|
# Service for getting train time HTML page from the Amtrak website
|
3
5
|
class TrainFetcher
|
@@ -20,21 +22,33 @@ module Amtrak
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def get
|
23
|
-
|
24
|
-
|
25
|
+
requests = [first_page.body]
|
26
|
+
|
27
|
+
(2..total_pages).each do |page|
|
28
|
+
requests << Amtrak::TrainFetcher::TrainPage.get(agent, page)
|
25
29
|
end
|
30
|
+
|
31
|
+
requests
|
26
32
|
end
|
27
33
|
|
28
34
|
def first_page
|
29
|
-
@first_page ||= Amtrak::TrainFetcher::MainPage.new(from, to, date: date)
|
35
|
+
@first_page ||= Amtrak::TrainFetcher::MainPage.new(agent, from, to, date: date)
|
30
36
|
end
|
31
37
|
|
32
|
-
def
|
33
|
-
@
|
38
|
+
def total_pages
|
39
|
+
return @total_pages if @total_pages
|
40
|
+
|
41
|
+
total_pages = first_page.total_pages
|
42
|
+
Amtrak.logger.debug "Total pages: #{total_pages}"
|
43
|
+
|
44
|
+
@total_pages = total_pages
|
34
45
|
end
|
35
46
|
|
36
|
-
def
|
37
|
-
@
|
47
|
+
def agent
|
48
|
+
@agent ||= Mechanize.new.tap do |m|
|
49
|
+
m.user_agent_alias = 'Mac Safari'
|
50
|
+
m.log = Amtrak.logger
|
51
|
+
end
|
38
52
|
end
|
39
53
|
end
|
40
54
|
end
|
@@ -1,13 +1,13 @@
|
|
1
|
-
require 'excon'
|
2
1
|
require 'date'
|
3
2
|
|
4
3
|
module Amtrak
|
5
4
|
class TrainFetcher
|
6
5
|
# Service for getting train time results/cookies from the Amtrak website
|
7
6
|
class MainPage
|
8
|
-
attr_reader :from, :to
|
7
|
+
attr_reader :agent, :from, :to
|
9
8
|
|
10
|
-
def initialize(from, to, date: nil)
|
9
|
+
def initialize(agent, from, to, date: nil)
|
10
|
+
@agent = agent
|
11
11
|
@from = from
|
12
12
|
@to = to
|
13
13
|
@date = date
|
@@ -16,20 +16,19 @@ module Amtrak
|
|
16
16
|
def request
|
17
17
|
retries ||= 3
|
18
18
|
_request
|
19
|
-
rescue
|
19
|
+
rescue Mechanize::ResponseCodeError
|
20
20
|
retries -= 1
|
21
|
-
|
21
|
+
raise if retries.zero?
|
22
|
+
|
23
|
+
retry
|
22
24
|
end
|
23
25
|
|
24
26
|
def _request
|
25
|
-
@request ||=
|
26
|
-
'
|
27
|
-
|
28
|
-
|
29
|
-
expects: [200]
|
27
|
+
@request ||= agent.post(
|
28
|
+
'https://tickets.amtrak.com/itd/amtrak',
|
29
|
+
request_body,
|
30
|
+
headers
|
30
31
|
)
|
31
|
-
rescue Excon::Errors::ClientError, Excon::Errors::ServerError => ex
|
32
|
-
raise Amtrak::TrainFetcher::Error, "#{ex.class} #{ex.message}"
|
33
32
|
end
|
34
33
|
|
35
34
|
def headers
|
@@ -37,17 +36,17 @@ module Amtrak
|
|
37
36
|
end
|
38
37
|
|
39
38
|
# rubocop:disable all
|
40
|
-
def
|
39
|
+
def request_body
|
41
40
|
{
|
42
|
-
"_handler=amtrak.presentation.handler.request.rail.AmtrakRailTrainStatusSearchRequestHandler/_xpath=/sessionWorkflow/productWorkflow[@product='Rail']" =>
|
41
|
+
"_handler=amtrak.presentation.handler.request.rail.AmtrakRailTrainStatusSearchRequestHandler/_xpath=/sessionWorkflow/productWorkflow[@product='Rail']" => "",
|
43
42
|
"/sessionWorkflow/productWorkflow[@product='Rail']/tripRequirements/journeyRequirements[1]/departDate.usdate" => departure_date,
|
44
|
-
"/sessionWorkflow/productWorkflow[@product='Rail']/tripRequirements/@trainStatusType" =>
|
45
|
-
"/sessionWorkflow/productWorkflow[@product='Rail']/tripRequirements/journeyRequirements[1]/departDate/@radioSelect" =>
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
"/sessionWorkflow/productWorkflow[@product='Rail']/tripRequirements/@trainStatusType" => "statusByCityPair",
|
44
|
+
"/sessionWorkflow/productWorkflow[@product='Rail']/tripRequirements/journeyRequirements[1]/departDate/@radioSelect" => "arrivalTime",
|
45
|
+
"requestor" => "amtrak.presentation.handler.page.rail.AmtrakRailGetTrainStatusPageHandler",
|
46
|
+
"xwdf_origin" => "/sessionWorkflow/productWorkflow[@product='Rail']/travelSelection/journeySelection[1]/departLocation/search",
|
47
|
+
"wdf_origin" => from.to_s,
|
48
|
+
"xwdf_destination" => "/sessionWorkflow/productWorkflow[@product='Rail']/travelSelection/journeySelection[1]/arriveLocation/search",
|
49
|
+
"wdf_destination" => to.to_s,
|
51
50
|
}
|
52
51
|
end
|
53
52
|
# rubocop:enable all
|
@@ -60,8 +59,8 @@ module Amtrak
|
|
60
59
|
@date ||= Date.today
|
61
60
|
end
|
62
61
|
|
63
|
-
def
|
64
|
-
request.
|
62
|
+
def body
|
63
|
+
request.body
|
65
64
|
end
|
66
65
|
|
67
66
|
def total_pages
|
@@ -69,11 +68,15 @@ module Amtrak
|
|
69
68
|
end
|
70
69
|
|
71
70
|
def extract_listing_length
|
72
|
-
|
71
|
+
if matches = body.match(/var availabilityLength = '(\d+)';/)
|
72
|
+
matches[1]
|
73
|
+
else
|
74
|
+
0
|
75
|
+
end
|
73
76
|
end
|
74
77
|
|
75
78
|
def release
|
76
|
-
|
79
|
+
body.match(/"Amtrak Release ([^"]+)"/)[1]
|
77
80
|
end
|
78
81
|
end
|
79
82
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'excon'
|
2
|
-
|
3
1
|
module Amtrak
|
4
2
|
class TrainFetcher
|
5
3
|
# Service for getting per page train time HTML from the Amtrak website
|
@@ -8,10 +6,10 @@ module Amtrak
|
|
8
6
|
new(*args).get
|
9
7
|
end
|
10
8
|
|
11
|
-
attr_reader :
|
9
|
+
attr_reader :agent, :page
|
12
10
|
|
13
|
-
def initialize(
|
14
|
-
@
|
11
|
+
def initialize(agent, page)
|
12
|
+
@agent = agent
|
15
13
|
@page = page
|
16
14
|
end
|
17
15
|
|
@@ -22,23 +20,28 @@ module Amtrak
|
|
22
20
|
def request
|
23
21
|
retries ||= 3
|
24
22
|
_request
|
25
|
-
rescue
|
23
|
+
rescue Mechanize::ResponseCodeError
|
26
24
|
retries -= 1
|
27
|
-
|
25
|
+
raise if retries.zero?
|
26
|
+
|
27
|
+
retry
|
28
28
|
end
|
29
29
|
|
30
30
|
def _request
|
31
|
-
@request ||=
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
@request ||= agent.get(
|
32
|
+
"https://tickets.amtrak.com/itd/amtrak/TrainStatusRequest?&_trainstatuspage=#{page}",
|
33
|
+
[],
|
34
|
+
nil,
|
35
|
+
headers
|
35
36
|
)
|
36
|
-
rescue Excon::Errors::ClientError, Excon::Errors::ServerError => ex
|
37
|
-
raise Amtrak::TrainFetcher::Error, "#{ex.class} #{ex.message}"
|
38
37
|
end
|
39
38
|
|
40
39
|
def headers
|
41
|
-
{
|
40
|
+
{
|
41
|
+
'ADRUM' => 'isAjax:true',
|
42
|
+
'X-Prototype-Version' => '1.6.0.3',
|
43
|
+
'X-Requested-With' => 'XMLHttpRequest'
|
44
|
+
}
|
42
45
|
end
|
43
46
|
|
44
47
|
def query
|
data/lib/amtrak/version.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Amtrak::TrainFetcher::MainPage do
|
4
|
+
let(:agent) { Mechanize.new }
|
4
5
|
describe '#departure_date' do
|
5
|
-
subject { described_class.new('', '', date: date) }
|
6
|
+
subject { described_class.new(nil, '', '', date: date) }
|
6
7
|
let(:date) { Date.parse('2014-11-12') }
|
7
8
|
|
8
9
|
it 'prints out a formatted date' do
|
@@ -11,7 +12,7 @@ describe Amtrak::TrainFetcher::MainPage do
|
|
11
12
|
end
|
12
13
|
|
13
14
|
describe '#date' do
|
14
|
-
subject { described_class.new('', '', date: date) }
|
15
|
+
subject { described_class.new(nil, '', '', date: date) }
|
15
16
|
|
16
17
|
context 'on an instance with a date' do
|
17
18
|
let(:date) { Date.parse('2014-11-12') }
|
@@ -34,7 +35,7 @@ describe Amtrak::TrainFetcher::MainPage do
|
|
34
35
|
|
35
36
|
describe '#total_pages' do
|
36
37
|
context 'when only one page exists on the website' do
|
37
|
-
subject { described_class.new('pvd', 'bby', date: Date.parse('
|
38
|
+
subject { described_class.new(agent, 'pvd', 'bby', date: Date.parse('2016-01-03')) }
|
38
39
|
|
39
40
|
it 'returns 1', :vcr do
|
40
41
|
expect(subject.total_pages).to eq(1)
|
@@ -42,34 +43,11 @@ describe Amtrak::TrainFetcher::MainPage do
|
|
42
43
|
end
|
43
44
|
|
44
45
|
context 'when more than one page exists on the website' do
|
45
|
-
subject { described_class.new('pvd', 'bby', date: Date.parse('
|
46
|
+
subject { described_class.new(agent, 'pvd', 'bby', date: Date.parse('2015-12-31')) }
|
46
47
|
|
47
48
|
it 'returns 2', :vcr do
|
48
49
|
expect(subject.total_pages).to eq(2)
|
49
50
|
end
|
50
51
|
end
|
51
52
|
end
|
52
|
-
|
53
|
-
describe '#session_id' do
|
54
|
-
subject { described_class.new('pvd', 'bby', date: Date.parse('2014-12-06')) }
|
55
|
-
it 'pulls the session id from the cookies', :vcr do
|
56
|
-
expect(subject.session_id).to eq('0000faPsyYZwp2n8Wb_4BDhukyg:187j59p07')
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
describe '#request' do
|
61
|
-
context 'when Excon raises' do
|
62
|
-
subject { described_class.new('', '') }
|
63
|
-
|
64
|
-
it 'returns a TrainFetcher::Error' do
|
65
|
-
expect(Excon).to receive(:post) { fail Excon::Errors::ClientError, '' }
|
66
|
-
expect { subject.request }.to raise_error(Amtrak::TrainFetcher::Error)
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'returns a TrainFetcher::Error' do
|
70
|
-
expect(Excon).to receive(:post) { fail Excon::Errors::ServerError, '' }
|
71
|
-
expect { subject.request }.to raise_error(Amtrak::TrainFetcher::Error)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
53
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Amtrak::TrainFetcher::TrainPage do
|
4
|
-
|
4
|
+
let(:agent) { Mechanize.new }
|
5
|
+
subject { described_class.get(agent, page) }
|
5
6
|
|
6
7
|
describe '#get', :vcr do
|
7
8
|
context 'when it works' do
|
8
|
-
let(:session_id) { '0000faPsyYZwp2n8Wb_4BDhukyg:187j59p07' }
|
9
9
|
let(:page) { '1' }
|
10
10
|
|
11
11
|
it 'includes various classes' do
|
@@ -14,24 +14,5 @@ describe Amtrak::TrainFetcher::TrainPage do
|
|
14
14
|
expect(subject).to include('resp_by_citypair_arrive_status_details')
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
18
|
-
context 'when Excon raises an error' do
|
19
|
-
let(:session_id) { '0000faPsyYZwp2n8Wb_4BDhukyg:187j59p07' }
|
20
|
-
let(:page) { '1' }
|
21
|
-
|
22
|
-
it 'reraises as a TrainFetcher::Error' do
|
23
|
-
expect(Excon).to receive(:get) {
|
24
|
-
fail Excon::Errors::ClientError, ''
|
25
|
-
}
|
26
|
-
expect { subject }.to raise_error(Amtrak::TrainFetcher::Error)
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'reraises as a TrainFetcher::Error' do
|
30
|
-
expect(Excon).to receive(:get) {
|
31
|
-
fail Excon::Errors::ServerError, ''
|
32
|
-
}
|
33
|
-
expect { subject }.to raise_error(Amtrak::TrainFetcher::Error)
|
34
|
-
end
|
35
|
-
end
|
36
17
|
end
|
37
18
|
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
describe Amtrak::TrainFetcher do
|
4
4
|
describe '.get', :vcr do
|
5
5
|
let(:output) do
|
6
|
-
described_class.get('pvd', 'bby', date: Date.parse('
|
6
|
+
described_class.get('pvd', 'bby', date: Date.parse('2015-12-31'))
|
7
7
|
end
|
8
8
|
|
9
9
|
it 'does the same as #get' do
|
@@ -16,7 +16,7 @@ describe Amtrak::TrainFetcher do
|
|
16
16
|
describe '#get', :vcr do
|
17
17
|
describe 'given a valid date and train stations' do
|
18
18
|
subject do
|
19
|
-
described_class.new('pvd', 'bby', date: Date.parse('
|
19
|
+
described_class.new('pvd', 'bby', date: Date.parse('2015-12-31'))
|
20
20
|
end
|
21
21
|
|
22
22
|
let(:output) { subject.get }
|
@@ -44,7 +44,7 @@ describe Amtrak::TrainFetcher do
|
|
44
44
|
|
45
45
|
describe 'given a valid date and invalid train stations' do
|
46
46
|
subject do
|
47
|
-
described_class.new('askdf', 'bby', date: Date.parse('
|
47
|
+
described_class.new('askdf', 'bby', date: Date.parse('2015-12-31'))
|
48
48
|
end
|
49
49
|
|
50
50
|
let(:output) { subject.get }
|
data/spec/amtrak_spec.rb
CHANGED
@@ -4,7 +4,7 @@ describe Amtrak do
|
|
4
4
|
context '.get' do
|
5
5
|
let(:from) { 'pvd' }
|
6
6
|
let(:to) { 'bby' }
|
7
|
-
let(:date) { Date.parse('
|
7
|
+
let(:date) { Date.parse('Sat, Jan 3, 2016') }
|
8
8
|
let(:output) { described_class.get(from, to, date: date) }
|
9
9
|
let(:expected) do
|
10
10
|
JSON.parse(
|
@@ -14,6 +14,7 @@ describe Amtrak do
|
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'returns a list of train times', :vcr do
|
17
|
+
puts JSON.pretty_generate(output)
|
17
18
|
expect(output).to eq(expected)
|
18
19
|
end
|
19
20
|
end
|
@@ -1 +1,54 @@
|
|
1
|
-
[
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"number": 194,
|
4
|
+
"departure": {
|
5
|
+
"date": "Sun, Jan 3 2016",
|
6
|
+
"scheduled_time": "8:25 pm",
|
7
|
+
"estimated_time": "8:29 pm"
|
8
|
+
},
|
9
|
+
"arrival": {
|
10
|
+
"date": "Sun, Jan 3 2016",
|
11
|
+
"scheduled_time": "9:10 pm",
|
12
|
+
"estimated_time": "9:00 pm"
|
13
|
+
}
|
14
|
+
},
|
15
|
+
{
|
16
|
+
"number": 2256,
|
17
|
+
"departure": {
|
18
|
+
"date": "Sun, Jan 3 2016",
|
19
|
+
"scheduled_time": "8:55 pm",
|
20
|
+
"estimated_time": ""
|
21
|
+
},
|
22
|
+
"arrival": {
|
23
|
+
"date": "Sun, Jan 3 2016",
|
24
|
+
"scheduled_time": "9:39 pm",
|
25
|
+
"estimated_time": ""
|
26
|
+
}
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"number": 2258,
|
30
|
+
"departure": {
|
31
|
+
"date": "Sun, Jan 3 2016",
|
32
|
+
"scheduled_time": "9:54 pm",
|
33
|
+
"estimated_time": "9:59 pm"
|
34
|
+
},
|
35
|
+
"arrival": {
|
36
|
+
"date": "Sun, Jan 3 2016",
|
37
|
+
"scheduled_time": "10:40 pm",
|
38
|
+
"estimated_time": "10:38 pm"
|
39
|
+
}
|
40
|
+
},
|
41
|
+
{
|
42
|
+
"number": 132,
|
43
|
+
"departure": {
|
44
|
+
"date": "Sun, Jan 3 2016",
|
45
|
+
"scheduled_time": "11:02 pm",
|
46
|
+
"estimated_time": "11:03 pm"
|
47
|
+
},
|
48
|
+
"arrival": {
|
49
|
+
"date": "Sun, Jan 3 2016",
|
50
|
+
"scheduled_time": "11:49 pm",
|
51
|
+
"estimated_time": "11:35 pm"
|
52
|
+
}
|
53
|
+
}
|
54
|
+
]
|