gares 2.0.0.pre.dev3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +34 -25
- data/gares.gemspec +3 -2
- data/lib/attribute_accessors.rb +22 -0
- data/lib/gares/base.rb +68 -41
- data/lib/gares/errors.rb +14 -0
- data/lib/gares/search.rb +42 -12
- data/lib/gares/station.rb +1 -1
- data/lib/gares/train.rb +43 -18
- data/lib/gares/version.rb +1 -1
- data/lib/gares.rb +3 -3
- data/lib/string_extensions.rb +20 -0
- data/spec/fixtures/87723197.json +21 -0
- data/spec/fixtures/LYD-arrivals.json +17 -0
- data/spec/fixtures/LYD-departures.json +17 -0
- data/spec/fixtures/bls.csv +21484 -0
- data/spec/fixtures/get-train-12345 +219 -0
- data/spec/fixtures/post-train-12345 +0 -0
- data/spec/gares/search_spec.rb +94 -57
- data/spec/gares/station_spec.rb +61 -0
- data/spec/gares/train_spec.rb +25 -4
- data/spec/spec_helper.rb +14 -16
- data/tasks/fixtures.rake +4 -3
- metadata +31 -21
- data/lib/gares/sales.rb +0 -15
- data/lib/gares/services.rb +0 -15
- data/lib/gares/string_extensions.rb +0 -22
- data/spec/fixtures/frabt +0 -330
- data/spec/fixtures/frabt-services-vente +0 -12
- data/spec/fixtures/frhco +0 -346
- data/spec/fixtures/frlpd +0 -349
- data/spec/fixtures/frlpd-services +0 -462
- data/spec/fixtures/frlpd-services-vente +0 -399
- data/spec/fixtures/frqxb-services +0 -386
- data/spec/fixtures/frqxb-services-vente +0 -367
- data/spec/fixtures/frxag +0 -333
- data/spec/fixtures/search +0 -5254
- data/spec/gares/gare_spec.rb +0 -79
- data/spec/gares/sales_spec.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7131f02cc2cf5cf1679f4019a4198c988b8747b2
|
4
|
+
data.tar.gz: ef830677495eeb6ce7d99d6eb3141e0bc1c3d453
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3731eb75501f062651d4d1eea46e3af54285d63cd196e090203c44987c17def765012c13a5b6161a9d84a27e11abbcb93ffdaac12e5d77c7e9f2f0ee629f282
|
7
|
+
data.tar.gz: 831a5b1ccc6ba09cac02e0b56e8ec1e4a6ccf875055d318c7d2f5e3714f7bd0b75e15b6aa8d538f29f11643aaed1c8d32e17d6a3e4d8046b1909fb4d35c59426
|
data/README.md
CHANGED
@@ -4,37 +4,39 @@
|
|
4
4
|
|
5
5
|
## Description
|
6
6
|
|
7
|
-
The Gares gem allows you to easily access publicly available data from gares-
|
8
|
-
It can also retrieve data from sncf.com for live train information.
|
7
|
+
The Gares gem allows you to easily access publicly available data from gares-sncf.com.
|
8
|
+
It can also retrieve data from sncf.com for live train information. Train station information comes from the public [CapitaineTrain database](https://github.com/capitainetrain/stations).
|
9
9
|
|
10
10
|
## Features
|
11
11
|
|
12
12
|
Gares currently features the following:
|
13
13
|
|
14
|
-
* Search for a station
|
15
|
-
* Retrieve station information
|
16
|
-
*
|
14
|
+
* Search for a train station by name or sncf_id
|
15
|
+
* Retrieve station information such as:
|
16
|
+
* Whether it has a self-service ticket machine
|
17
|
+
* Next departing / arriving trains with live information (platform numbers, delay)
|
18
|
+
* Retrieve a specific train live information given its number and its date
|
17
19
|
|
18
20
|
## Examples
|
19
21
|
|
20
22
|
### Station:
|
21
23
|
|
22
|
-
g = Gares::Station.
|
24
|
+
g = Gares::Station.find_by_sncf_id("frlpd")
|
23
25
|
|
24
26
|
g.name
|
25
27
|
#=> "Lyon Part-Dieu"
|
26
28
|
|
27
|
-
g.wifi?
|
28
|
-
#=> true
|
29
|
-
|
30
29
|
g.has_borne?
|
31
30
|
#=> true
|
32
31
|
|
33
|
-
g.services.first
|
34
|
-
#=> "Services à la clientèle"
|
35
|
-
|
36
32
|
[g.latitude, g.longitude]
|
37
|
-
#=> [45.
|
33
|
+
#=> [45.760568, 4.859991]
|
34
|
+
|
35
|
+
g.departuring_trains.first
|
36
|
+
#=> #<Gares::Train heure="05:50" num=9852 type="TGV" origdest=#<Gares::Station name="Bruxelles" ...> ...>
|
37
|
+
|
38
|
+
g.departuring_trains.first.platform
|
39
|
+
#=> "K"
|
38
40
|
|
39
41
|
See the [`Gares::Base` class documentation](http://www.rubydoc.info/github/paulrbr/gares/master/Gares/Base) for all available data on a station.
|
40
42
|
|
@@ -45,20 +47,27 @@ See the [`Gares::Base` class documentation](http://www.rubydoc.info/github/paulr
|
|
45
47
|
g.stations.size
|
46
48
|
#=> 28
|
47
49
|
|
48
|
-
# or
|
50
|
+
# or directly a class method on the Gares::Station object
|
49
51
|
|
50
52
|
stations = Gares::Station.search("lyon")
|
51
53
|
station = stations.last
|
52
54
|
|
53
55
|
station.name
|
54
|
-
#=> "
|
56
|
+
#=> "Lyon St-Exupéry TGV"
|
57
|
+
|
58
|
+
# or by sncf_id
|
59
|
+
|
60
|
+
station = Gares::Station.find_by_sncf_id("frjdq")
|
61
|
+
|
62
|
+
station.name
|
63
|
+
#=> "Lyon St-Exupéry TGV"
|
55
64
|
|
56
|
-
### Train information:
|
65
|
+
### Train live information:
|
57
66
|
|
58
67
|
train = Gares::Train.new(11641, Time.now)
|
59
68
|
|
60
69
|
train.departure.station
|
61
|
-
#=> #<Gares::Station
|
70
|
+
#=> #<Gares::Station @name="Paris-Gare-de-l’Est" ...>
|
62
71
|
|
63
72
|
train.departure.departure_date
|
64
73
|
#=> 2015-04-25 06:42:00 +0200
|
@@ -89,10 +98,10 @@ Or if you want to use it in a project add this to your `Gemfile`:
|
|
89
98
|
|
90
99
|
As this gem uses external content from gare-en-mouvement.com and sncf.com, the test suite uses a set of
|
91
100
|
pre-defined fixture files in `spec/fixtures`. These fixtures are
|
92
|
-
copies of gares-
|
101
|
+
copies of gares-sncf.com and sncf.com pages used in tests.
|
93
102
|
|
94
103
|
Run bundle install to install all dependencies, including fakeweb, which
|
95
|
-
will serve the fixture files instead of doing actual requests to gares-
|
104
|
+
will serve the fixture files instead of doing actual requests to gares-sncf.com or sncf.com.
|
96
105
|
|
97
106
|
$ bundle install
|
98
107
|
|
@@ -100,7 +109,7 @@ Next, simple run `rake` to run the entire test suite.
|
|
100
109
|
|
101
110
|
### Running against real data
|
102
111
|
|
103
|
-
It's possible to run the test suite directly against gares-
|
112
|
+
It's possible to run the test suite directly against gares-sncf.com or sncf.com. This has
|
104
113
|
two disadvantages:
|
105
114
|
|
106
115
|
1. Tests will be slow
|
@@ -122,15 +131,15 @@ When you run the test suite now, it will use the updated fixture files.
|
|
122
131
|
Neither I, nor any developer who contributed to this project, accept any kind of
|
123
132
|
liability for your use of this library.
|
124
133
|
|
125
|
-
gares-
|
134
|
+
gares-sncf.com and sncf.com certainly does not permit use of its data by third parties without their consent.
|
126
135
|
|
127
136
|
Using this library for anything other than limited personal use may result
|
128
|
-
in an IP ban to the gares-
|
137
|
+
in an IP ban to the gares-sncf.com or sncf.com website.
|
129
138
|
|
130
|
-
_This gem is not endorsed or affiliated with gares-
|
139
|
+
_This gem is not endorsed or affiliated with gares-sncf.com, nor SNCF Inc, nor Capitaine Train._
|
131
140
|
|
132
141
|
## License
|
133
142
|
|
134
|
-
|
143
|
+
Code licensed under [MIT-LICENSE](https://github.com/paulrbr/gares/blob/master/MIT-LICENSE)
|
135
144
|
|
136
|
-
|
145
|
+
Stations database [ODbL LICENSE](https://raw.githubusercontent.com/capitainetrain/stations/master/LICENCE.txt)
|
data/gares.gemspec
CHANGED
@@ -9,8 +9,8 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ['Paul Bonaud']
|
10
10
|
s.email = ['paul+gh@bonaud.fr']
|
11
11
|
s.homepage = 'http://github.com/paulrbr/gares'
|
12
|
-
s.summary = %q(Easily access the publicly available information on gares-
|
13
|
-
s.description = %q(Easily use Ruby or the command line to find information on gares-
|
12
|
+
s.summary = %q(Easily access the publicly available information on gares-sncf.com and sncf.com.)
|
13
|
+
s.description = %q(Easily use Ruby or the command line to find information on gares-sncf.com and sncf.com.)
|
14
14
|
|
15
15
|
s.rubyforge_project = 'gares'
|
16
16
|
|
@@ -24,6 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_dependency 'smarter_csv', '~> 1.0'
|
25
25
|
s.add_dependency 'unidecoder', '~> 1.1'
|
26
26
|
s.add_dependency 'httparty', '~> 0.13'
|
27
|
+
s.add_dependency 'trie'
|
27
28
|
|
28
29
|
s.add_development_dependency 'rake'
|
29
30
|
s.add_development_dependency 'rspec'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Fork of https://raw.githubusercontent.com/rails/rails/master/activesupport/lib/active_support/core_ext/module/attribute_accessors.rb
|
2
|
+
class Module
|
3
|
+
def mattr_reader(*syms)
|
4
|
+
syms.each do |sym|
|
5
|
+
raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
|
6
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
7
|
+
@@#{sym} = nil unless defined? @@#{sym}
|
8
|
+
|
9
|
+
def self.#{sym}
|
10
|
+
@@#{sym}
|
11
|
+
end
|
12
|
+
EOS
|
13
|
+
|
14
|
+
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
15
|
+
def #{sym}
|
16
|
+
@@#{sym}
|
17
|
+
end
|
18
|
+
EOS
|
19
|
+
class_variable_set("@@#{sym}", yield) if block_given?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/gares/base.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Gares
|
2
|
-
# Represents
|
2
|
+
# Represents a station on gares-sncf.com
|
3
3
|
class Base < Hashie::Dash
|
4
4
|
property :id
|
5
5
|
property :name
|
@@ -33,66 +33,93 @@ module Gares
|
|
33
33
|
property :'info:de'
|
34
34
|
property :'info:it'
|
35
35
|
property :same_as
|
36
|
+
property :has_bls
|
36
37
|
|
38
|
+
OPEN_DATA_SNCF = "https://ressources.data.sncf.com/api/records/1.0/search?dataset=referentiel-gares-voyageurs&q=%s"
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
def services
|
41
|
-
@services ||= Services.new(sncf_id: sncf_id)
|
42
|
-
@services.all
|
43
|
-
end
|
44
|
-
|
45
|
-
def sales
|
46
|
-
@sales ||= Sales.new(sncf_id: sncf_id)
|
47
|
-
@sales.all
|
48
|
-
end
|
49
|
-
|
50
|
-
def horaires
|
51
|
-
document.search('ul.ouverture_heure li').
|
52
|
-
map { |horaire| horaire.inner_html } rescue []
|
53
|
-
end
|
54
|
-
alias opening_hours horaires
|
55
|
-
|
56
|
-
# Whether the gare has a defibrillator or not
|
57
|
-
def defibrillateur?
|
58
|
-
!document.at('div.defibrillateur').nil?
|
59
|
-
end
|
60
|
-
alias defibrillator? defibrillateur?
|
61
|
-
|
62
|
-
# Whether the gare is equipped with wifi or not
|
63
|
-
def wifi?
|
64
|
-
!document.at('div.wifi').nil?
|
65
|
-
end
|
40
|
+
GARES_SNCF = "http://www.gares-sncf.com/fr/train-times/%s/%s/gl"
|
66
41
|
|
42
|
+
# Whether this station has a "borne" (yellow self-service ticket machine)
|
43
|
+
# @return [Boolean]
|
67
44
|
def has_borne?
|
68
|
-
|
45
|
+
has_bls == "t"
|
69
46
|
end
|
70
47
|
|
71
|
-
# deprecated
|
48
|
+
# @deprecated
|
72
49
|
def slug
|
50
|
+
warn "[DEPRECATION] favor the 'sncf_id' method instead of 'slug'."
|
73
51
|
sncf_id.downcase
|
74
52
|
end
|
75
53
|
|
76
|
-
# deprecated
|
54
|
+
# @deprecated
|
77
55
|
def lat
|
56
|
+
warn "[DEPRECATION] favor the 'latitude' method instead of 'lat'."
|
78
57
|
latitude
|
79
58
|
end
|
80
59
|
|
81
|
-
# deprecated
|
60
|
+
# @deprecated
|
82
61
|
def long
|
62
|
+
warn "[DEPRECATION] favor the 'longitude' method instead of 'long'."
|
83
63
|
longitude
|
84
64
|
end
|
85
65
|
|
66
|
+
# List of the next departing trains from this station.
|
67
|
+
# @param refresh [Boolean] whether to fetch fresh data from gares-sncf.com or not.
|
68
|
+
# @return [Array<Train>]
|
69
|
+
def departures(refresh = false)
|
70
|
+
if tvs
|
71
|
+
trains(:departure, refresh)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
alias departing_trains departures
|
75
|
+
|
76
|
+
# List of the next arriving trains in this station.
|
77
|
+
# @param refresh [Boolean] whether to fetch fresh data from gares-sncf.com or not.
|
78
|
+
# @return [Array<Train>]
|
79
|
+
def arrivals(refresh = false)
|
80
|
+
if tvs
|
81
|
+
trains(:arrival, refresh)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
alias arriving_trains arrivals
|
85
|
+
|
86
86
|
private
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
def tvs
|
89
|
+
if uic8_sncf
|
90
|
+
@tvs ||= self.class.open_data_sncf(uic8_sncf, :tvs)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def trains(direction = :departure, refresh = false)
|
95
|
+
variable = "@#{direction}".to_sym
|
96
|
+
if tvs && (refresh || instance_variable_get(variable).nil?)
|
97
|
+
raw_trains = self.class.external_gares_sncf(tvs, direction, refresh)
|
98
|
+
all_trains = raw_trains.map do |raw_train|
|
99
|
+
raw_train[:num] = raw_train[:num].to_i
|
100
|
+
raw_train[:date] = Time.now
|
101
|
+
Gares::Train.new(raw_train)
|
102
|
+
end
|
103
|
+
instance_variable_set(variable, all_trains)
|
104
|
+
end
|
105
|
+
instance_variable_get(variable)
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.open_data_sncf(uic8_sncf, field)
|
109
|
+
@open_data ||= {}
|
110
|
+
@open_data[uic8_sncf] ||= JSON.parse(open(OPEN_DATA_SNCF % ("%010d" % uic8_sncf).to_s).read, :symbolize_names => true)[:records]
|
111
|
+
unless @open_data[uic8_sncf].empty?
|
112
|
+
@open_data[uic8_sncf].first[:fields][field]
|
113
|
+
end
|
91
114
|
end
|
92
115
|
|
93
|
-
|
94
|
-
|
95
|
-
|
116
|
+
def self.external_gares_sncf(tvs, direction = :departure, refresh = false)
|
117
|
+
@gares_sncf ||= {}
|
118
|
+
@gares_sncf[tvs] ||= {}
|
119
|
+
if refresh || @gares_sncf.nil? || @gares_sncf[direction].nil?
|
120
|
+
@gares_sncf[tvs][direction] = JSON.parse(open(GARES_SNCF % [direction, tvs]).read, :symbolize_names => true)[:trains]
|
121
|
+
end
|
122
|
+
@gares_sncf[tvs][direction]
|
96
123
|
end
|
97
124
|
|
98
125
|
# Convenience method for search (by name)
|
@@ -101,8 +128,8 @@ module Gares
|
|
101
128
|
end
|
102
129
|
|
103
130
|
# Convenience method for search_by_sncf_id
|
104
|
-
def self.
|
105
|
-
Gares::Search.new(query, :sncf_id).stations
|
131
|
+
def self.find_by_sncf_id(query)
|
132
|
+
Gares::Search.new(query, :sncf_id).stations.first
|
106
133
|
end
|
107
134
|
end # Base
|
108
135
|
end # Gares
|
data/lib/gares/errors.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Gares
|
2
|
+
# All errors from this gem will inherit from this one.
|
3
|
+
class Error < StandardError
|
4
|
+
end
|
5
|
+
class UnsupportedIndex < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
# Raised when requesting a train that does not exist.
|
9
|
+
class TrainNotFound < Error
|
10
|
+
end
|
11
|
+
# Raised when requesting a station that does not exist.
|
12
|
+
class StationNotFound < Error
|
13
|
+
end
|
14
|
+
end
|
data/lib/gares/search.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
+
require 'trie'
|
2
|
+
|
1
3
|
module Gares
|
2
4
|
# Search Gares-en-mouvement for a station name
|
3
5
|
class Search < StationList
|
6
|
+
@@trie = nil
|
7
|
+
mattr_reader :trie
|
8
|
+
|
4
9
|
attr_reader :query
|
5
10
|
|
6
11
|
# This is the stations database from capitainetrain.com
|
7
|
-
GARES_LIST_URL = "https://raw.githubusercontent.com/
|
12
|
+
GARES_LIST_URL = "https://raw.githubusercontent.com/paulrbr/stations/stations-with-bls/stations.csv"
|
8
13
|
|
9
|
-
# List of keywords to ignore while searching
|
10
|
-
IGNORE_KEYWORDS = ["ST", "SAINT", "GARE", "SNCF"]
|
11
14
|
# Initialize a new Station search with the specified query
|
12
15
|
#
|
13
16
|
# search = Gares::Search.new("Aix")
|
@@ -15,7 +18,10 @@ module Gares
|
|
15
18
|
# Gares::Search is lazy loaded, meaning that unless you access the +stations+
|
16
19
|
# attribute, no remomte query is made.
|
17
20
|
#
|
21
|
+
# Search can by done via the :name or :sncf_id field given in parameter.
|
22
|
+
# Defaults to the :name field.
|
18
23
|
def initialize(query, field = :name)
|
24
|
+
fail UnsupportedIndex unless %w(name sncf_id).include?(field.to_s)
|
19
25
|
@query = query
|
20
26
|
@by = field
|
21
27
|
end
|
@@ -26,22 +32,46 @@ module Gares
|
|
26
32
|
@stations ||= (exact_match? ? parse_station : parse_stations)
|
27
33
|
end
|
28
34
|
|
29
|
-
|
30
|
-
|
31
|
-
def data
|
32
|
-
@data ||= self.class.query.map { |raw_station| Gares::Station.new(raw_station) }
|
35
|
+
def self.find(str)
|
36
|
+
trie.find_prefix(str)
|
33
37
|
end
|
34
38
|
|
39
|
+
private
|
40
|
+
|
35
41
|
def result
|
36
|
-
|
37
|
-
@
|
38
|
-
|
42
|
+
query = self.class.simplify(@query)
|
43
|
+
if @raw_results.nil?
|
44
|
+
@raw_results = self.class.find(query).values
|
45
|
+
# try first keyword if nothing found
|
46
|
+
@raw_results = @raw_results.empty? ? self.class.find(query.split(" ").first).values : @raw_results
|
39
47
|
end
|
48
|
+
@result ||= @raw_results.map { |raw_station| Gares::Station.new(raw_station) }
|
40
49
|
end
|
41
50
|
|
42
|
-
def self.
|
43
|
-
|
51
|
+
def self.simplify(str)
|
52
|
+
str.to_ascii.downcase
|
53
|
+
.gsub(/\bsaint\b/, "st")
|
54
|
+
.gsub(/[^a-z]/, " ")
|
55
|
+
.gsub(/\s+/, " ")
|
56
|
+
.strip
|
57
|
+
end
|
58
|
+
|
59
|
+
# Read stations.csv file into memory into a Trie data structure
|
60
|
+
def self.load_data
|
61
|
+
if @@trie.nil?
|
62
|
+
raw_data ||= SmarterCSV.process(open(GARES_LIST_URL), col_sep: ";")
|
63
|
+
trie = Trie.new
|
64
|
+
|
65
|
+
raw_data.each do |raw_station|
|
66
|
+
next if raw_station[:name].nil? || raw_station[:sncf_id].nil? || raw_station[:uic].nil?
|
67
|
+
trie.insert(simplify(raw_station[:name]), raw_station)
|
68
|
+
trie.insert(simplify(raw_station[:sncf_id]), raw_station)
|
69
|
+
end
|
70
|
+
|
71
|
+
@@trie = trie
|
72
|
+
end
|
44
73
|
end
|
74
|
+
load_data
|
45
75
|
|
46
76
|
def parse_station
|
47
77
|
[result.first]
|
data/lib/gares/station.rb
CHANGED
data/lib/gares/train.rb
CHANGED
@@ -2,42 +2,64 @@ require "net/http"
|
|
2
2
|
require "uri"
|
3
3
|
|
4
4
|
module Gares
|
5
|
-
# Represents
|
6
|
-
class Train
|
7
|
-
|
5
|
+
# Represents a train from http://www.sncf.com/fr/horaires-info-trafic/train
|
6
|
+
class Train < Hashie::Dash
|
7
|
+
property :origdest
|
8
|
+
property :num, required: true
|
9
|
+
property :date, required: true
|
10
|
+
property :type
|
11
|
+
property :picto
|
12
|
+
property :voie
|
13
|
+
property :voie_attr
|
14
|
+
property :heure
|
15
|
+
property :etat
|
16
|
+
property :retard
|
17
|
+
property :infos
|
8
18
|
|
9
|
-
# Initialize a new Train object with it's number and departure date
|
19
|
+
# Initialize a new Train object with at least it's number and departure date
|
10
20
|
#
|
11
|
-
# train = Gares::Train.new(6704, Time.parse('2015-04-15'))
|
21
|
+
# train = Gares::Train.new(num: 6704, date: Time.parse('2015-04-15'))
|
12
22
|
#
|
13
23
|
# Gares::Train objects are lazy loaded, meaning that no HTTP request
|
14
|
-
# will be performed when a new object is created. An HTTP request is made
|
15
|
-
# Only when you use an accessor that needs
|
16
|
-
def initialize(
|
17
|
-
fail "Please provide a train number" unless
|
18
|
-
fail "Please provide a departure date" unless date.is_a?(Time)
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
# will be performed when a new object is created. An HTTP request is made
|
25
|
+
# Only when you use an accessor that needs remote data.
|
26
|
+
def initialize(*arguments)
|
27
|
+
fail "Please provide a train number" unless arguments.first[:num].is_a?(Integer)
|
28
|
+
fail "Please provide a departure date" unless arguments.first[:date].is_a?(Time)
|
29
|
+
|
30
|
+
if arguments.first[:origdest]
|
31
|
+
arguments.first[:origdest] = Gares::Station.search(arguments.first[:origdest]).first
|
32
|
+
end
|
33
|
+
|
34
|
+
super(*arguments)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Integer] The train number
|
38
|
+
def number
|
39
|
+
num
|
22
40
|
end
|
23
41
|
|
24
42
|
# @return [TrainStop] The departure point of the train
|
25
43
|
def departure
|
26
|
-
@departure ||= TrainStop.new(document.at('tr.itinerary-start'),
|
44
|
+
@departure ||= TrainStop.new(document.at('tr.itinerary-start'), date)
|
27
45
|
end
|
28
46
|
|
29
47
|
# @return [Array<TrainStop>] A list of all stops between departure and arrival stations.
|
30
48
|
def stops
|
31
|
-
@stops ||= document.css('tr.itinerary-stop').map { |stop| TrainStop.new(stop,
|
49
|
+
@stops ||= document.css('tr.itinerary-stop').map { |stop| TrainStop.new(stop, date) }
|
32
50
|
end
|
33
51
|
|
34
52
|
# @return [TrainStop] The arrival point of the train
|
35
53
|
def arrival
|
36
|
-
@arrival ||= TrainStop.new(document.at('tr.itinerary-end'),
|
54
|
+
@arrival ||= TrainStop.new(document.at('tr.itinerary-end'), date)
|
37
55
|
end
|
38
56
|
|
39
57
|
def delayed?
|
40
|
-
([departure] + stops + [arrival]).any?(&:delayed?)
|
58
|
+
retard || ([departure] + stops + [arrival]).any?(&:delayed?)
|
59
|
+
end
|
60
|
+
|
61
|
+
def platform
|
62
|
+
voie
|
41
63
|
end
|
42
64
|
|
43
65
|
private
|
@@ -49,11 +71,14 @@ module Gares
|
|
49
71
|
# Returns a new Nokogiri document for parsing.
|
50
72
|
def document
|
51
73
|
if !@document
|
52
|
-
@document = Nokogiri::HTML(self.class.request_sncf(
|
74
|
+
@document = Nokogiri::HTML(self.class.request_sncf(number, date))
|
53
75
|
if !itinerary_available?
|
54
76
|
@document = Nokogiri::HTML(self.class.request_sncf_itinerary(0))
|
55
77
|
end
|
56
78
|
end
|
79
|
+
if @document.at('#no_results')
|
80
|
+
fail TrainNotFound, @document.at('#no_results b').inner_html
|
81
|
+
end
|
57
82
|
@document
|
58
83
|
end
|
59
84
|
|
data/lib/gares/version.rb
CHANGED
data/lib/gares.rb
CHANGED
@@ -9,14 +9,14 @@ require 'hashie'
|
|
9
9
|
require 'smarter_csv'
|
10
10
|
require 'unidecoder'
|
11
11
|
require 'httparty'
|
12
|
+
require 'attribute_accessors'
|
13
|
+
require 'string_extensions'
|
12
14
|
|
15
|
+
require 'gares/errors'
|
13
16
|
require 'gares/base'
|
14
17
|
require 'gares/station'
|
15
18
|
require 'gares/station_list'
|
16
|
-
require 'gares/sales'
|
17
|
-
require 'gares/services'
|
18
19
|
require 'gares/search'
|
19
|
-
require 'gares/string_extensions'
|
20
20
|
require 'gares/train'
|
21
21
|
require 'gares/train_stop'
|
22
22
|
require 'gares/version'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module StringExtensions
|
4
|
+
# Unescape HTML
|
5
|
+
def unescape_html
|
6
|
+
CGI.unescapeHTML(encode('UTF-8'))
|
7
|
+
end
|
8
|
+
|
9
|
+
# Strip tags
|
10
|
+
def strip_tags
|
11
|
+
gsub(/<\/?[^>]*>/, '')
|
12
|
+
end
|
13
|
+
|
14
|
+
# Strips out whitespace then tests if the string is empty.
|
15
|
+
def blank?
|
16
|
+
strip.empty?
|
17
|
+
end unless method_defined?(:blank?)
|
18
|
+
end
|
19
|
+
|
20
|
+
String.send :include, StringExtensions
|
@@ -0,0 +1,21 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Server: nginx
|
3
|
+
Date: Sat, 02 May 2015 18:11:46 GMT
|
4
|
+
Content-Type: application/json; charset=utf-8
|
5
|
+
Transfer-Encoding: chunked
|
6
|
+
Connection: keep-alive
|
7
|
+
X-RateLimit-Remaining: 4954
|
8
|
+
Access-Control-Allow-Headers: Authorization, X-Requested-With, Origin, ODS-API-Analytics-App, ODS-Widgets-Version
|
9
|
+
Content-Language: fr
|
10
|
+
Access-Control-Max-Age: 1000
|
11
|
+
Expires: Fri, 01 Jan 1990 00:00:00 GMT
|
12
|
+
Vary: Accept-Language, Cookie, Host
|
13
|
+
X-RateLimit-Limit: 5000
|
14
|
+
Pragma: no-cache
|
15
|
+
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
|
16
|
+
Access-Control-Allow-Origin: *
|
17
|
+
Access-Control-Allow-Methods: POST, GET, OPTIONS
|
18
|
+
X-RateLimit-Reset: 1430611200
|
19
|
+
X-UA-Compatible: IE=edge
|
20
|
+
|
21
|
+
{"nhits": 1, "parameters": {"dataset": ["referentiel-gares-voyageurs"], "q": "0087723197", "rows": 10, "format": "json"}, "records": [{"datasetid": "referentiel-gares-voyageurs", "recordid": "2b09e1b10aa14b82a8febc5269a07231d5359108", "fields": {"departement": "Rh\u00f4ne", "commune": "Lyon", "uic": "0087723197", "code_postal": "69003", "code_gare": "00534", "region": "Rh\u00f4ne-Alpes", "tvs": "LYD", "departement_commune": "69-123", "agence_gare": "AG Centre Est Rh\u00f4ne Alpin", "nombre_plateformes": "1", "niveau_de_service": "3", "segment_drg": "a", "region_sncf": "REGION RHONE-ALPES", "intitule_gare": "Lyon Part Dieu"}, "record_timestamp": "2015-02-09T10:19:37.908942"}]}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
HTTP/1.1 200 OK
|
2
|
+
Date: Sat, 02 May 2015 22:45:19 GMT
|
3
|
+
Expires: Sun, 19 Nov 1978 05:00:00 GMT
|
4
|
+
Last-Modified: Sat, 02 May 2015 22:43:57 GMT
|
5
|
+
Cache-Control: no-cache, must-revalidate, post-check=0, pre-check=0
|
6
|
+
ETag: "1430606637"
|
7
|
+
Content-Language: fr
|
8
|
+
Content-Type: application/json
|
9
|
+
Content-Length: 4434
|
10
|
+
X-Varnish-Request: /fr/train-times/arrival/LYD/gl
|
11
|
+
X-Varnish-Key: /fr/train-times/arrival/LYD/glgares-sncf.com
|
12
|
+
X-Varnish: 901909486 901908872
|
13
|
+
Age: 82
|
14
|
+
X-Varnish-Cache: HIT
|
15
|
+
X-Varnish-Hits: 1
|
16
|
+
|
17
|
+
{"trains":[{"origdest":"ROANNE","num":"886605","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"07:20","etat":"","retard":"","infos":""},{"origdest":"ST ETIENNE","num":"886806","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"07:36","etat":"","retard":"","infos":""},{"origdest":"BOURG EN B.","num":"49601","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"bus","heure":"07:37","etat":"","retard":"","infos":""},{"origdest":"GRENOBLE","num":"17604","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"07:44","etat":"","retard":"","infos":""},{"origdest":"AMBERIEU","num":"886707","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"07:48","etat":"","retard":"","infos":""},{"origdest":"MARSEILLE","num":"6866","type":"TGV","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_9.png","voie":"","voie_attr":"","heure":"07:56","etat":"","retard":"","infos":""},{"origdest":"DIJON VILLE","num":"6801","type":"TGV","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_9.png","voie":"","voie_attr":"","heure":"07:56","etat":"","retard":"","infos":""},{"origdest":"MARSEILLE","num":"5065","type":"TGV","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_9.png","voie":"","voie_attr":"","heure":"08:25","etat":"","retard":"","infos":""},{"origdest":"MARSEILLE","num":"9854","type":"TGV","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_9.png","voie":"","voie_attr":"","heure":"08:25","etat":"","retard":"","infos":""},{"origdest":"PARIS","num":"9241","type":"TGV","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_9.png","voie":"","voie_attr":"","heure":"08:26","etat":"","retard":"","infos":""},{"origdest":"ST ETIENNE","num":"886810","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"08:36","etat":"","retard":"","infos":""},{"origdest":"BOURG EN B.","num":"889415","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"08:40","etat":"","retard":"","infos":""},{"origdest":"GRENOBLE","num":"17608","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"08:44","etat":"","retard":"","infos":""},{"origdest":"DIJON VILLE","num":"17803","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"08:47","etat":"","retard":"","infos":""},{"origdest":"ROANNE","num":"886627","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"08:48","etat":"","retard":"","infos":""},{"origdest":"MARSEILLE","num":"5144","type":"TGV","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_9.png","voie":"","voie_attr":"","heure":"08:50","etat":"","retard":"","infos":""},{"origdest":"MONTPELLIER","num":"9862","type":"TGV","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_9.png","voie":"","voie_attr":"","heure":"08:54","etat":"","retard":"","infos":""},{"origdest":"LILLE EUROPE","num":"5103","type":"TGV","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_9.png","voie":"","voie_attr":"","heure":"09:00","etat":"","retard":"","infos":""},{"origdest":"LILLE EUROPE","num":"5105","type":"TGV","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_9.png","voie":"","voie_attr":"","heure":"09:00","etat":"","retard":"","infos":""},{"origdest":"CHAMBERY","num":"18542","type":"TER","picto":"\/sites\/default\/files\/field_taxo_trans_picto_horaires\/2015-03\/30_sncf_nb_5.png","voie":"","voie_attr":"","heure":"09:10","etat":"","retard":"","infos":""}],"updated":"00:10"}
|