gares 1.1.1 → 2.0.0.pre.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Guardfile +1 -1
- data/README.md +8 -6
- data/gares.gemspec +1 -0
- data/lib/gares/base.rb +70 -44
- data/lib/gares/errors.rb +14 -0
- data/lib/gares/sales.rb +1 -2
- data/lib/gares/search.rb +36 -14
- data/lib/gares/services.rb +2 -2
- data/lib/gares/station_list.rb +1 -3
- data/lib/gares/train.rb +28 -3
- data/lib/gares/train_stop.rb +15 -2
- data/lib/gares/version.rb +1 -1
- data/lib/gares.rb +2 -0
- data/spec/fixtures/get-train-12345 +219 -0
- data/spec/fixtures/get-train-6815 +6 -11
- data/spec/fixtures/get-train-6815-0 +0 -0
- data/spec/fixtures/get-train-6815-0-data +627 -0
- data/spec/fixtures/get-train-6815-1 +0 -0
- data/spec/fixtures/get-train-6815-1-data +367 -0
- data/spec/fixtures/post-train-12345 +0 -0
- data/spec/fixtures/post-train-6815 +0 -0
- data/spec/fixtures/stations.csv +21484 -0
- data/spec/gares/sales_spec.rb +3 -1
- data/spec/gares/search_spec.rb +105 -40
- data/spec/gares/station_spec.rb +64 -0
- data/spec/gares/train_spec.rb +50 -9
- data/spec/spec_helper.rb +15 -2
- data/tasks/fixtures.rake +18 -0
- metadata +27 -5
- data/spec/gares/gare_spec.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f87e7dc8b8d185548c8dcc4c12c789e07802caea
|
4
|
+
data.tar.gz: 971aa5a58e033f79d41f901899bc6d862c56f5f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4432ec3fa4011d82f5b22f63aa647e7b75fca52fa2222335032f9cbb0b5078bcb59008d9d9bfd4fe9474143248d212beac197c2c5758b871519968bed5a8fd38
|
7
|
+
data.tar.gz: 9afd2bd055b35a12e10b15b10d9a29c4208744c0ae1a119c39403f4b8ccfb9ad06c66e1f6d92c8b6bdd09113e6d9c9a068ee9b41412cbad0586127a4ced2f589
|
data/Guardfile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
group 'gem' do
|
2
2
|
guard 'rspec', :cmd => "bundle exec rspec", :all_on_start => false, :all_after_pass => false, :failed_mode => :focus do
|
3
3
|
watch(%r{^spec/.+_spec\.rb$})
|
4
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/gares/#{m[1]}_spec.rb" }
|
4
|
+
watch(%r{^lib/gares/(.+)\.rb$}) { |m| "spec/gares/#{m[1]}_spec.rb" }
|
5
5
|
end
|
6
6
|
end
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ Gares currently features the following:
|
|
22
22
|
g = Gares::Station.new("frlpd")
|
23
23
|
|
24
24
|
g.name
|
25
|
-
#=> "Lyon Part
|
25
|
+
#=> "Lyon Part-Dieu"
|
26
26
|
|
27
27
|
g.wifi?
|
28
28
|
#=> true
|
@@ -33,7 +33,7 @@ Gares currently features the following:
|
|
33
33
|
g.services.first
|
34
34
|
#=> "Services à la clientèle"
|
35
35
|
|
36
|
-
[g.
|
36
|
+
[g.latitude, g.longitude]
|
37
37
|
#=> [45.760281, 4.859801]
|
38
38
|
|
39
39
|
See the [`Gares::Base` class documentation](http://www.rubydoc.info/github/paulrbr/gares/master/Gares/Base) for all available data on a station.
|
@@ -43,7 +43,7 @@ See the [`Gares::Base` class documentation](http://www.rubydoc.info/github/paulr
|
|
43
43
|
g = Gares::Search.new("Aix")
|
44
44
|
|
45
45
|
g.stations.size
|
46
|
-
#=>
|
46
|
+
#=> 28
|
47
47
|
|
48
48
|
# or
|
49
49
|
|
@@ -51,14 +51,14 @@ See the [`Gares::Base` class documentation](http://www.rubydoc.info/github/paulr
|
|
51
51
|
station = stations.last
|
52
52
|
|
53
53
|
station.name
|
54
|
-
#=> "
|
54
|
+
#=> "Aéroport-Lyon-Saint-Exupéry"
|
55
55
|
|
56
56
|
### Train information:
|
57
57
|
|
58
58
|
train = Gares::Train.new(11641, Time.now)
|
59
59
|
|
60
60
|
train.departure.station
|
61
|
-
#=> #<Gares::Station:0x000f0000000000 @
|
61
|
+
#=> #<Gares::Station:0x000f0000000000 @name="Paris-Gare-de-l’Est", ...>
|
62
62
|
|
63
63
|
train.departure.departure_date
|
64
64
|
#=> 2015-04-25 06:42:00 +0200
|
@@ -70,7 +70,7 @@ See the [`Gares::Base` class documentation](http://www.rubydoc.info/github/paulr
|
|
70
70
|
#=> false
|
71
71
|
|
72
72
|
train.arrival.station.name
|
73
|
-
#=> "Culmont
|
73
|
+
#=> "Culmont-Chalindrey"
|
74
74
|
|
75
75
|
train.arrival.platform
|
76
76
|
#=> "B"
|
@@ -132,3 +132,5 @@ _This gem is not endorsed or affiliated with gares-en-mouvement.com, nor SNCF, I
|
|
132
132
|
## License
|
133
133
|
|
134
134
|
See [MIT-LICENSE](https://github.com/paulrbr/gares/blob/master/MIT-LICENSE)
|
135
|
+
|
136
|
+
Station database [ODbL LICENSE](https://raw.githubusercontent.com/capitainetrain/stations/master/LICENCE.txt)
|
data/gares.gemspec
CHANGED
data/lib/gares/base.rb
CHANGED
@@ -1,92 +1,118 @@
|
|
1
1
|
module Gares
|
2
2
|
# Represents something on gare-en-mouvement.com
|
3
|
-
class Base
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
3
|
+
class Base < Hashie::Dash
|
4
|
+
property :id
|
5
|
+
property :name
|
6
|
+
property :slug
|
7
|
+
property :sncf_id
|
8
|
+
property :longitude
|
9
|
+
property :latitude
|
10
|
+
property :uic
|
11
|
+
property :uic8_sncf
|
12
|
+
property :parent_station_id
|
13
|
+
property :is_city
|
14
|
+
property :country
|
15
|
+
property :is_main_station
|
16
|
+
property :time_zone
|
17
|
+
property :is_suggestable
|
18
|
+
property :sncf_is_enabled
|
19
|
+
property :idtgv_id
|
20
|
+
property :idtgv_is_enabled
|
21
|
+
property :db_id
|
22
|
+
property :db_is_enabled
|
23
|
+
property :idbus_id
|
24
|
+
property :idbus_is_enabled
|
25
|
+
property :ouigo_id
|
26
|
+
property :ouigo_is_enabled
|
27
|
+
property :trenitalia_id
|
28
|
+
property :trenitalia_is_enabled
|
29
|
+
property :ntv_id
|
30
|
+
property :ntv_is_enabled
|
31
|
+
property :'info:fr'
|
32
|
+
property :'info:en'
|
33
|
+
property :'info:de'
|
34
|
+
property :'info:it'
|
35
|
+
property :same_as
|
36
|
+
|
37
|
+
# @deprecated
|
30
38
|
def services
|
31
|
-
|
39
|
+
warn "[DEPRECATION] since gares-en-mouvement.com does not exist anymore."
|
40
|
+
@services ||= Services.new(sncf_id: sncf_id)
|
32
41
|
@services.all
|
33
42
|
end
|
34
43
|
|
44
|
+
# @deprecated
|
35
45
|
def sales
|
36
|
-
|
46
|
+
warn "[DEPRECATION] since gares-en-mouvement.com does not exist anymore."
|
47
|
+
@sales ||= Sales.new(sncf_id: sncf_id)
|
37
48
|
@sales.all
|
38
49
|
end
|
39
50
|
|
51
|
+
# @deprecated
|
40
52
|
def horaires
|
53
|
+
warn "[DEPRECATION] since gares-en-mouvement.com does not exist anymore."
|
41
54
|
document.search('ul.ouverture_heure li').
|
42
55
|
map { |horaire| horaire.inner_html } rescue []
|
43
56
|
end
|
44
57
|
alias opening_hours horaires
|
45
58
|
|
46
59
|
# Whether the gare has a defibrillator or not
|
60
|
+
# @deprecated
|
47
61
|
def defibrillateur?
|
62
|
+
warn "[DEPRECATION] since gares-en-mouvement.com does not exist anymore."
|
48
63
|
!document.at('div.defibrillateur').nil?
|
49
64
|
end
|
50
65
|
alias defibrillator? defibrillateur?
|
51
66
|
|
52
67
|
# Whether the gare is equipped with wifi or not
|
68
|
+
# @deprecated
|
53
69
|
def wifi?
|
70
|
+
warn "[DEPRECATION] since gares-en-mouvement.com does not exist anymore."
|
54
71
|
!document.at('div.wifi').nil?
|
55
72
|
end
|
56
73
|
|
74
|
+
# @deprecated
|
57
75
|
def has_borne?
|
76
|
+
warn "[DEPRECATION] since gares-en-mouvement.com does not exist anymore."
|
58
77
|
sales.any? { |sales_service| sales_service =~ /bornes?.libre.service/i }
|
59
78
|
end
|
60
79
|
|
61
|
-
#
|
62
|
-
def
|
63
|
-
|
64
|
-
@name
|
65
|
-
else
|
66
|
-
@name = document.at('h1').inner_html.gsub(NAME, '') rescue nil
|
67
|
-
end
|
80
|
+
# deprecated
|
81
|
+
def slug
|
82
|
+
sncf_id.downcase
|
68
83
|
end
|
69
84
|
|
70
|
-
|
85
|
+
# deprecated
|
86
|
+
def lat
|
87
|
+
latitude
|
88
|
+
end
|
71
89
|
|
72
|
-
|
73
|
-
|
74
|
-
|
90
|
+
# deprecated
|
91
|
+
def long
|
92
|
+
longitude
|
75
93
|
end
|
76
94
|
|
95
|
+
private
|
96
|
+
|
77
97
|
# Returns a new Nokogiri document for parsing.
|
78
98
|
def document
|
79
|
-
@document ||= Nokogiri::HTML(
|
99
|
+
@document ||= Nokogiri::HTML(self.class.external_data(sncf_id))
|
80
100
|
end
|
81
101
|
|
82
102
|
# Use HTTParty to fetch the raw HTML for this gare.
|
83
|
-
|
84
|
-
|
103
|
+
# @deprecated
|
104
|
+
def self.external_data(sncf_id, page = :"votre-gare")
|
105
|
+
open("http://www.gares-en-mouvement.com/fr/#{sncf_id.downcase}/#{page}")
|
85
106
|
end
|
86
107
|
|
87
|
-
# Convenience method for search
|
108
|
+
# Convenience method for search (by name)
|
88
109
|
def self.search(query)
|
89
110
|
Gares::Search.new(query).stations
|
90
111
|
end
|
112
|
+
|
113
|
+
# Convenience method for search_by_sncf_id
|
114
|
+
def self.search_by_sncf_id(query)
|
115
|
+
Gares::Search.new(query, :sncf_id).stations
|
116
|
+
end
|
91
117
|
end # Base
|
92
118
|
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/sales.rb
CHANGED
data/lib/gares/search.rb
CHANGED
@@ -3,11 +3,11 @@ module Gares
|
|
3
3
|
class Search < StationList
|
4
4
|
attr_reader :query
|
5
5
|
|
6
|
-
# This is
|
7
|
-
GARES_LIST_URL = "https://
|
6
|
+
# This is the stations database from capitainetrain.com
|
7
|
+
GARES_LIST_URL = "https://raw.githubusercontent.com/capitainetrain/stations/master/stations.csv"
|
8
8
|
|
9
9
|
# List of keywords to ignore while searching
|
10
|
-
IGNORE_KEYWORDS = ["ST"]
|
10
|
+
IGNORE_KEYWORDS = ["ST", "SAINT", "GARE", "SNCF"]
|
11
11
|
# Initialize a new Station search with the specified query
|
12
12
|
#
|
13
13
|
# search = Gares::Search.new("Aix")
|
@@ -15,8 +15,12 @@ module Gares
|
|
15
15
|
# Gares::Search is lazy loaded, meaning that unless you access the +stations+
|
16
16
|
# attribute, no remomte query is made.
|
17
17
|
#
|
18
|
-
|
18
|
+
# Search can by done via the :name or :sncf_id field given in parameter.
|
19
|
+
# Defaults to the :name field.
|
20
|
+
def initialize(query, field = :name)
|
21
|
+
fail UnsupportedIndex unless %w(name sncf_id).include?(field.to_s)
|
19
22
|
@query = query
|
23
|
+
@by = field
|
20
24
|
end
|
21
25
|
|
22
26
|
# Returns an array of Gares::Station objects in order to easily search result yielded.
|
@@ -27,24 +31,42 @@ module Gares
|
|
27
31
|
|
28
32
|
private
|
29
33
|
|
30
|
-
def
|
31
|
-
@
|
34
|
+
def result
|
35
|
+
@raw_result ||= case @by
|
36
|
+
when :name
|
37
|
+
keywords = @query.to_ascii.split(/[ -]/).select { |keyword| !IGNORE_KEYWORDS.include?(keyword.upcase) }
|
38
|
+
regexp_query = keywords.join(".*")
|
39
|
+
self.class.data(@by).select do |index, v|
|
40
|
+
index && index =~ /#{regexp_query}/i
|
41
|
+
end
|
42
|
+
when :sncf_id
|
43
|
+
{ @query.downcase => self.class.data(@by)[@query.downcase] }
|
44
|
+
end
|
45
|
+
|
46
|
+
@result ||= @raw_result.map { |_, raw_station| Gares::Station.new(raw_station) }
|
32
47
|
end
|
33
48
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
49
|
+
# Read stations.csv file into memory
|
50
|
+
# @param index either :name or :sncf_id
|
51
|
+
# @return [Hash<String, Hash>] list of stations indexed in a Hash
|
52
|
+
def self.data(index)
|
53
|
+
@@raw_data ||= SmarterCSV.process(open(GARES_LIST_URL), col_sep: ";")
|
54
|
+
case index
|
55
|
+
when :name
|
56
|
+
@@data_by_name ||= index_data(@@raw_data, index)
|
57
|
+
when :sncf_id
|
58
|
+
@@data_by_sncf_id ||= index_data(@@raw_data, index)
|
38
59
|
end
|
39
60
|
end
|
40
61
|
|
41
|
-
def self.
|
42
|
-
|
62
|
+
def self.index_data(data, by)
|
63
|
+
data.map do |raw_station|
|
64
|
+
[raw_station[by].to_ascii.downcase, raw_station] if raw_station[by] && raw_station[:uic]
|
65
|
+
end.compact.to_h
|
43
66
|
end
|
44
67
|
|
45
68
|
def parse_station
|
46
|
-
|
47
|
-
[Gares::Station.new(station.slug, station.name)]
|
69
|
+
[result.first]
|
48
70
|
end
|
49
71
|
|
50
72
|
def exact_match?
|
data/lib/gares/services.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Gares
|
2
|
+
# @deprecated
|
2
3
|
class Services < Base
|
3
4
|
|
4
5
|
def all
|
@@ -9,8 +10,7 @@ module Gares
|
|
9
10
|
private
|
10
11
|
|
11
12
|
def document
|
12
|
-
@document ||= Nokogiri::HTML(
|
13
|
-
@slug, :"services-en-gare/service/"))
|
13
|
+
@document ||= Nokogiri::HTML(self.class.external_data(sncf_id, :"services-en-gare/service/"))
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
data/lib/gares/station_list.rb
CHANGED
data/lib/gares/train.rb
CHANGED
@@ -42,19 +42,44 @@ module Gares
|
|
42
42
|
|
43
43
|
private
|
44
44
|
|
45
|
+
def itinerary_available?
|
46
|
+
document.css('ul.itinerary-details').size > 0
|
47
|
+
end
|
48
|
+
|
45
49
|
# Returns a new Nokogiri document for parsing.
|
46
50
|
def document
|
47
|
-
|
51
|
+
if !@document
|
52
|
+
@document = Nokogiri::HTML(self.class.request_sncf(@number, @date))
|
53
|
+
if !itinerary_available?
|
54
|
+
@document = Nokogiri::HTML(self.class.request_sncf_itinerary(0))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
if @document.at('#no_results')
|
58
|
+
fail TrainNotFound, @document.at('#no_results b').inner_html
|
59
|
+
end
|
60
|
+
@document
|
48
61
|
end
|
49
62
|
|
50
63
|
def self.request_sncf(number, date)
|
51
64
|
uri = URI.parse("http://www.sncf.com/sncf/train")
|
52
65
|
response = Net::HTTP.post_form(uri, {"numeroTrain" => number, "date" => date.strftime("%d/%m/%Y")})
|
53
|
-
cookies = response.get_fields('Set-Cookie').map { |cookie| cookie.split(";").first }.join(";")
|
66
|
+
@cookies = response.get_fields('Set-Cookie').map { |cookie| cookie.split(";").first }.join(";")
|
54
67
|
|
55
68
|
uri = URI.parse("http://www.sncf.com/en/horaires-info-trafic/train/resultats")
|
56
69
|
req = Net::HTTP::Get.new(uri.path)
|
57
|
-
req.add_field("Cookie", cookies)
|
70
|
+
req.add_field("Cookie", @cookies)
|
71
|
+
Net::HTTP.new(uri.host, uri.port).start { |http| http.request(req) }.body
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.request_sncf_itinerary(index)
|
75
|
+
uri = URI.parse("http://www.sncf.com/sncf/train/displayDetailTrain?idItineraire=#{index}")
|
76
|
+
req = Net::HTTP::Get.new(uri)
|
77
|
+
req.add_field("Cookie", @cookies) if @cookies
|
78
|
+
Net::HTTP.new(uri.host, uri.port).start { |http| http.request(req) }.body
|
79
|
+
|
80
|
+
uri = URI.parse("http://www.sncf.com/en/horaires-info-trafic/train/resultats?#{index}")
|
81
|
+
req = Net::HTTP::Get.new(uri)
|
82
|
+
req.add_field("Cookie", @cookies) if @cookies
|
58
83
|
Net::HTTP.new(uri.host, uri.port).start { |http| http.request(req) }.body
|
59
84
|
end
|
60
85
|
|
data/lib/gares/train_stop.rb
CHANGED
@@ -74,9 +74,22 @@ module Gares
|
|
74
74
|
raw_name = node.at('td.stations div.station').inner_html.strip
|
75
75
|
stations = Station.search(raw_name)
|
76
76
|
@station = if stations.size > 1
|
77
|
+
raw_name.gsub!(/[ -]/, '.*')
|
78
|
+
exact_match = /^#{raw_name}$/i
|
79
|
+
begin_match = /^#{raw_name}/i
|
80
|
+
end_match = /#{raw_name}$/
|
81
|
+
middle_match = /#{raw_name}/i
|
77
82
|
stations.find do |station|
|
78
|
-
name
|
79
|
-
|
83
|
+
station.name.to_ascii.match(exact_match)
|
84
|
+
end ||
|
85
|
+
stations.find do |station|
|
86
|
+
station.name.to_ascii.match(begin_match)
|
87
|
+
end ||
|
88
|
+
stations.find do |station|
|
89
|
+
station.name.to_ascii.match(end_match)
|
90
|
+
end ||
|
91
|
+
stations.find do |station|
|
92
|
+
station.name.to_ascii.match(middle_match)
|
80
93
|
end
|
81
94
|
else
|
82
95
|
stations.first
|
data/lib/gares/version.rb
CHANGED
data/lib/gares.rb
CHANGED
@@ -6,9 +6,11 @@ require 'rubygems'
|
|
6
6
|
require 'nokogiri'
|
7
7
|
require 'json'
|
8
8
|
require 'hashie'
|
9
|
+
require 'smarter_csv'
|
9
10
|
require 'unidecoder'
|
10
11
|
require 'httparty'
|
11
12
|
|
13
|
+
require 'gares/errors'
|
12
14
|
require 'gares/base'
|
13
15
|
require 'gares/station'
|
14
16
|
require 'gares/station_list'
|