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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a13a56ca07337138e8c27caaeef89cd4ef6f4bf5
4
- data.tar.gz: 0f0bd8cc2897185e17a06d0612df95dcb600db40
3
+ metadata.gz: 7131f02cc2cf5cf1679f4019a4198c988b8747b2
4
+ data.tar.gz: ef830677495eeb6ce7d99d6eb3141e0bc1c3d453
5
5
  SHA512:
6
- metadata.gz: 8e552311ca4763dd804e95504c6e07876a51937e535afaf103424025ffee1b18d4a9f43b2c45aad32ffe2cc19a4bf1686642979097937d0b4b93cb56d49171b0
7
- data.tar.gz: a91a6217481c551191637c6d38c0d8dd8b9304eaa8a57d1ab2422bbe0499e9deefa322f81269887802557ec5ff8f666218db0608a9f7e98afeb63b5ff1fe59ea
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-en-mouvement.com.
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
- * Search for a train to get live information about it
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.new("frlpd")
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.760281, 4.859801]
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
- #=> "Aéroport-Lyon-Saint-Exupéry"
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:0x000f0000000000 @name="Paris-Gare-de-l’Est", ...>
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-en-mouvement.com and sncf.com pages used in tests.
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-en-mouvement.com or sncf.com.
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-en-mouvement.com or sncf.com. This has
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-en-mouvement.com and sncf.com certainly does not permit use of its data by third parties without their consent.
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-en-mouvement.com or sncf.com website.
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-en-mouvement.com, nor SNCF, Inc._
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
- See [MIT-LICENSE](https://github.com/paulrbr/gares/blob/master/MIT-LICENSE)
143
+ Code licensed under [MIT-LICENSE](https://github.com/paulrbr/gares/blob/master/MIT-LICENSE)
135
144
 
136
- Station database [ODbL LICENSE](https://raw.githubusercontent.com/capitainetrain/stations/master/LICENCE.txt)
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-en-mouvement.com.)
13
- s.description = %q(Easily use Ruby or the command line to find information on gares-en-mouvement.com.)
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 something on gare-en-mouvement.com
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
- NAME = 'En direct de '
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
- sales.any? { |sales_service| sales_service =~ /bornes?.libre.service/i }
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
- # Returns a new Nokogiri document for parsing.
89
- def document
90
- @document ||= Nokogiri::HTML(self.class.external_data(sncf_id))
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
- # Use HTTParty to fetch the raw HTML for this gare.
94
- def self.external_data(sncf_id, page = :"votre-gare")
95
- open("http://www.gares-en-mouvement.com/fr/#{sncf_id.downcase}/#{page}")
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.search_by_sncf_id(query)
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
@@ -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/capitainetrain/stations/master/stations.csv"
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
- private
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
- keywords = @query.split(/[ -]/).select { |keyword| !IGNORE_KEYWORDS.include?(keyword.upcase) }
37
- @result ||= data.select do |station|
38
- station.send(@by) && station.send(@by).to_ascii =~ /#{keywords.join(".*")}/i
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.query
43
- @data ||= SmarterCSV.process(open(GARES_LIST_URL), col_sep: ";")
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
@@ -1,5 +1,5 @@
1
1
  module Gares
2
- # Represents a station on gares-en-mouvement.com
2
+ # Represents a station on gares-sncf.com
3
3
  class Station < Base
4
4
  end # Station
5
5
  end # Gares
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 something a train from http://www.sncf.com/fr/horaires-info-trafic/train
6
- class Train
7
- attr_accessor :date, :number
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 (once)
15
- # Only when you use an accessor that needs the remote data.
16
- def initialize(number, date)
17
- fail "Please provide a train number" unless number.is_a?(Integer)
18
- fail "Please provide a departure date" unless date.is_a?(Time)
19
-
20
- @number = number
21
- @date = date
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'), @date)
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, @date) }
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'), @date)
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(@number, @date))
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
@@ -1,3 +1,3 @@
1
1
  module Gares
2
- VERSION = '2.0.0-dev3'
2
+ VERSION = '2.0.0'
3
3
  end
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"}