gares 2.0.0.pre.dev3 → 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/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"}
|