transilien_microservices 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d9ce413dfd4786a986a8fe3226cef622df78e9f9
4
+ data.tar.gz: 3b672ac27a336f667d79fa1f73543d9012a84bb6
5
+ SHA512:
6
+ metadata.gz: 0c673cb9635c16509f58f0829002111cfdf40302ff62d0252f5f27dac5ec6d393f171074e1d16096224d20cedcb7d9460608689fe5fd0e8265d594d6a8258253
7
+ data.tar.gz: 70b3ebb0084748339843dd5b64fd33b4cec56d5cf1c5f37be79a2b7062171b3b0b81d5104f63a6804641914ca1e829e4c9b4eba7fdc471e1546e37a34ab0be73
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ InstalledFiles
7
+ _yardoc
8
+ coverage
9
+ doc/
10
+ lib/bundler/man
11
+ pkg
12
+ rdoc
13
+ spec/reports
14
+ test/tmp
15
+ test/version_tmp
16
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p0
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in transilien_microservices.gemspec
4
+ gemspec
5
+
6
+ # Dev only
7
+ group :test do
8
+ gem 'rspec'
9
+ gem 'guard-rspec'
10
+ gem 'pry'
11
+ gem 'pry-doc'
12
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,67 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ transilien_microservices (0.0.1)
5
+ faraday (>= 0.8.4)
6
+ nokogiri (>= 1.5.5)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ celluloid (0.15.2)
12
+ timers (~> 1.1.0)
13
+ coderay (1.0.9)
14
+ diff-lcs (1.1.3)
15
+ faraday (0.8.4)
16
+ multipart-post (~> 1.1)
17
+ ffi (1.9.0)
18
+ formatador (0.2.4)
19
+ guard (2.1.1)
20
+ formatador (>= 0.2.4)
21
+ listen (~> 2.1)
22
+ lumberjack (~> 1.0)
23
+ pry (>= 0.9.12)
24
+ thor (>= 0.18.1)
25
+ guard-rspec (2.5.4)
26
+ guard (>= 1.1)
27
+ rspec (~> 2.11)
28
+ listen (2.1.1)
29
+ celluloid (>= 0.15.2)
30
+ rb-fsevent (>= 0.9.3)
31
+ rb-inotify (>= 0.9)
32
+ lumberjack (1.0.4)
33
+ method_source (0.8.2)
34
+ multipart-post (1.1.5)
35
+ nokogiri (1.5.9)
36
+ pry (0.9.12.2)
37
+ coderay (~> 1.0.5)
38
+ method_source (~> 0.8)
39
+ slop (~> 3.4)
40
+ pry-doc (0.4.6)
41
+ pry (>= 0.9)
42
+ yard (>= 0.8)
43
+ rb-fsevent (0.9.3)
44
+ rb-inotify (0.9.2)
45
+ ffi (>= 0.5.0)
46
+ rspec (2.12.0)
47
+ rspec-core (~> 2.12.0)
48
+ rspec-expectations (~> 2.12.0)
49
+ rspec-mocks (~> 2.12.0)
50
+ rspec-core (2.12.0)
51
+ rspec-expectations (2.12.0)
52
+ diff-lcs (~> 1.1.3)
53
+ rspec-mocks (2.12.0)
54
+ slop (3.4.6)
55
+ thor (0.18.1)
56
+ timers (1.1.0)
57
+ yard (0.8.7.2)
58
+
59
+ PLATFORMS
60
+ ruby
61
+
62
+ DEPENDENCIES
63
+ guard-rspec
64
+ pry
65
+ pry-doc
66
+ rspec
67
+ transilien_microservices!
data/Guardfile ADDED
@@ -0,0 +1,24 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14
+ watch('config/routes.rb') { "spec/routing" }
15
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
16
+
17
+ # Capybara features specs
18
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
19
+
20
+ # Turnip features and steps
21
+ watch(%r{^spec/acceptance/(.+)\.feature$})
22
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
23
+ end
24
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012-2013 Thomas Lecavelier
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # TransilienMicroservices
2
+
3
+ Ruby implementation of SNCF Transilien Microservices.
4
+
5
+ Here the original service documentation: http://test.data-sncf.com/index.php/transilien.html/api.22.22-micro-services.html
6
+
7
+ These services let you know the theoric offer on Transilien service.
8
+
9
+ Disclamer: The gem only intend to implements the API. I'll create a "easy" wrapper to access these data in a convenient way very soon.
10
+
11
+ ## Installation
12
+
13
+ Gem developped with ruby 2.0.0, should work with ruby 1.9.3
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'transilien_microservices'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install transilien_microservices
26
+
27
+ ## Usage
28
+
29
+ The best way to access data is certainly querying through StopArea: start by fetching them.
30
+
31
+ stop_areas = Transilien::StopArea.find
32
+
33
+ Every Transilien "main" objects have the same attributes:
34
+
35
+ stop_areas.first.inspect
36
+ => "#<Transilien::StopArea external_code=\"DUA8738221\" name=\"LA DEFENSE GRANDE ARCHE\" >"
37
+
38
+ * "name" is a "public" name. Can't be filtered directly through Transilien API
39
+ * "external_code" is the true "key" to use in queries.
40
+
41
+ They have in bonus #payload method which return the node used to build this object.
42
+
43
+ If you want to find a commercial line that stops by two StopArea, say: Val d'Argenteuil (DUA8738179) and Paris Saint Lazare (DUA8738400) you can query this way:
44
+
45
+ val_stlaz_lines = Transilien::Line.find(stop_area_external_code: {and: ['DUA8738400', 'DUA8738179']})
46
+
47
+ Ok, but to tell the truth, I doubt that you don't bother about the way: you're going from Val to StLaz, not the inverse, so precise it:
48
+
49
+ val_to_stlaz_lines = Transilien::Line.find(destination_external_code: 'DUA8738400', stop_area_external_code: 'DUA8738179')
50
+
51
+ You get an Array of Transilien::Line fullfilling your wish.
52
+
53
+ Staying on that example, we'll stay with the "biggest" Line of the set: "Mantes la Jolie => Gare St Lazare via CONFLANS" DUA800854044
54
+ To get ALL the stops served by this Line:
55
+
56
+ Transilien::StopArea.find(line_external_code: 'DUA800854044')
57
+
58
+ Ok, that's fun. But Transilien is all about train and departures. What are the trains going from Val d’Argenteuil to Paris Saint Lazare? Transilien::VehicleJourney is all about it
59
+
60
+ instant = Time.new
61
+ start_time = Time.local(instant.year, instant.month, instant.day, 17, 30)
62
+ end_time = Time.local(instant.year, instant.month, instant.day, 18, 45)
63
+ Transilien::VehicleJourney.find stop_area_external_code: {and: ['DUA8738400', 'DUA8738179'], date: Transilien.date(instant), start_time: Transilien.time(start_time), end_time: Transilien.time(end_time) }
64
+
65
+ Yeah! Better. You still have a problem: this give you all the journey starting between start_time and end_time on date instant, but don't give a fuck to your way.
66
+
67
+ Ready to forget what you just learnt? Go back a little bit before this point: a Line instance always have at least two Route (one way, the other). And its finder understand a convenient param: CheckOrder. If set to 1, and 2 stops are given, Route returned will honor the given way by stops order:
68
+
69
+ routes_stlaz_val = Transilien::Route.find(stop_area_external_code: {and:['DUA8738400','DUA8738179']}, check_order: 1)
70
+
71
+ Here you have only Route stoping by DUA8738400 then DUA8738179, not the inverse.
72
+
73
+ Now it'll be easy to get VehicleJourney matching your needs. The same VehicleJourney will become:
74
+
75
+ Transilien::VehicleJourney.find route_external_code: routes_stlaz_val.map(&:external_code), date: Transilien.date(instant), start_time: Transilien.time(start_time), end_time: Transilien.time(end_time)
76
+
77
+ Easier, isn't it? Now take every Stop and keep only your matching StopArea: you'll get your hours of departures and arrivals :)
78
+
79
+ ## Contributing
80
+
81
+ 1. Fork it
82
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
83
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
84
+ 4. Push to the branch (`git push origin my-new-feature`)
85
+ 5. Create new Pull Request
86
+
87
+ NOTA: you're a beginner gem dev? This command may help you:
88
+
89
+ pry -Ilib -rtransilien_microservices
90
+
91
+ (you can replace pry with irb if you're not a good person…)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,2 @@
1
+ class Transilien::Company < Transilien::MicroService
2
+ end
@@ -0,0 +1,18 @@
1
+ class Transilien::Line < Transilien::MicroService
2
+ def mode_type
3
+ @mode_type ||= Transilien::ModeType.from_node(payload.at('ModeType'), access_time)
4
+ end
5
+ def network
6
+ @network ||= Transilien::Network.from_node(payload.at('Network'), access_time)
7
+ end
8
+
9
+ # I don't have a clue what is about…
10
+ def forward
11
+ payload.at('Forward')
12
+ end
13
+
14
+ # I don't have a clue what is about…
15
+ def backward
16
+ payload.at('Backward')
17
+ end
18
+ end
@@ -0,0 +1,202 @@
1
+ require 'uri'
2
+ require 'faraday'
3
+ require 'nokogiri'
4
+ require 'time' # Time.parse for access_time in specialized instances
5
+
6
+ class Transilien::MicroService
7
+ API_HOST = 'ms.api.transilien.com'
8
+ API_URI = URI.parse("http://#{API_HOST}/")
9
+
10
+ attr_accessor :name
11
+ attr_accessor :external_code
12
+
13
+ attr_accessor :access_time
14
+ attr_accessor :payload
15
+
16
+ class << self
17
+
18
+ Default_cache_duration = 300 # in second
19
+
20
+ attr_accessor :caches
21
+ attr_accessor :query_cache
22
+
23
+ def http(uri = API_URI)
24
+ @http ||= Faraday.new(:url => uri) do |faraday|
25
+ # TODO give option to setup faraday
26
+ faraday.request :url_encoded # form-encode POST params
27
+ #faraday.response :logger # log requests to STDOUT
28
+ faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
29
+ end
30
+ end
31
+
32
+ def find_from_full_query_cache(filters)
33
+ @query_cache ||= {}
34
+ if filters[:force_refresh]
35
+ @query_cache.delete(filters.to_s)
36
+ end
37
+ if @query_cache[filters.to_s] && (@query_cache[filters.to_s][:cached_at].to_i + Default_cache_duration > Time.now.to_i)
38
+ return @query_cache[filters.to_s][:payload]
39
+ end
40
+ end
41
+
42
+ # Iterative search on per instance search. That's HARD.
43
+ # WIP idea: since filters are prefixed by operator (AND by default), we can deal key by key. Collection on the first run is the matching
44
+ # instances on that uniq key. The result will be the last collection returned. Potentially nil.
45
+ def find_from_query_caches(filters, collection = nil, operator = nil)
46
+ if collection.nil?
47
+ # FIRST RUN
48
+ filters.keys.each do |key|
49
+ collection = [query_cache({key: key, value: filters[key]})]
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ # /?action=LineList&StopAreaExternalCode=DUA8754309;DUA8754513|and&RouteExternalCode=DUA8008030781013;DUA8008031050001|or
56
+ # -> find(:stop_area_external_code => { :and => ['DUA8754309', 'DUA8754513'] }, :route_external_code => { :or => ['DUA8008030781013', 'DUA8008031050001'] })
57
+ def find(filters = {}, options = {})
58
+ collection = find_from_full_query_cache(filters)
59
+ collection ||= find_from_query_caches(filters)
60
+
61
+ self.filters = filters
62
+ response = self.http.get(action_param, params)
63
+ puts('== Request: ')
64
+ puts(action_param.inspect)
65
+ puts(params.inspect)
66
+ puts(response.env[:url].inspect)
67
+ body = response.body
68
+ collection = []
69
+ doc = Nokogiri.XML(body)
70
+ return errors(doc) unless errors(doc).empty?
71
+ request_time = Time.parse(response.headers[:date])
72
+ doc.xpath(action_instance_xpath).each do |node|
73
+ item = from_node(node, request_time)
74
+ collection << item
75
+ end
76
+ @query_cache[filters.to_s] = { payload: collection, cached_at: request_time }
77
+ collection
78
+ end
79
+
80
+ def from_node(node, access_time)
81
+ item = new
82
+ item.payload = node
83
+
84
+ # common stuff
85
+ item.external_code = node["#{action_component}ExternalCode"]
86
+ item.name = node["#{action_component}Name"]
87
+ item.access_time = access_time
88
+
89
+ cache_it(item)
90
+
91
+ item
92
+ end
93
+
94
+ def cache_keys
95
+ [:name, :external_code]
96
+ end
97
+
98
+ def cache_it(item)
99
+ @caches ||= {}
100
+ cache_keys.each do |k|
101
+ @caches[k] ||= {}
102
+ @caches[k][item.send(k)] = item
103
+ end
104
+ item
105
+ end
106
+
107
+ def query_cache(query = nil)
108
+ return @caches unless query
109
+ q = query || {}
110
+ q[:key] ||= 'name'
111
+ return nil if @caches.nil? || @caches[action_component].nil?
112
+ (@caches[action_component][q[:key]] || []).each do |item|
113
+ return item if item.name == q[:value]
114
+ end
115
+ end
116
+
117
+ def errors(doc)
118
+ @errors ||= begin
119
+ @errors = []
120
+ doc.xpath('/Errors/Error').each do |err_node|
121
+ err = Transilien::MicroService::Error.new
122
+ err.code = err_node['code']
123
+ err.message = err_node.content
124
+ err.request = { params: params, action: action_param}
125
+ @errors << err
126
+ end
127
+ @errors
128
+ end
129
+ end
130
+
131
+
132
+ def action_component
133
+ self.to_s.split('::').last
134
+ end
135
+
136
+ def action
137
+ "#{action_component}List"
138
+ end
139
+
140
+ def action_param
141
+ "/?action=#{action}"
142
+ end
143
+
144
+ def action_instance_xpath
145
+ "/Action#{action}/#{action}/#{action_component}"
146
+ end
147
+
148
+ def params
149
+ return {} if filters.empty?
150
+ final = {}
151
+ @filters.each do |filter, filter_value|
152
+ final_filter = filter.to_s.split('_').map(&:capitalize).join
153
+ if filter_value.is_a?(Hash)
154
+ filter_value.each_pair do |operator, values|
155
+ ok_operators = [:and, :or]
156
+ raise ArgumentError.new("Operator #{operator} unknown. Should be one of #{ok_operators.inspect}.") unless ok_operators.include?(operator.to_sym)
157
+ final_values = [values].flatten.compact.join(';')
158
+ final[final_filter] = "#{final_values}|#{operator.to_s}"
159
+ end
160
+ elsif filter_value.is_a?(Array)
161
+ # By default, consider OR operator when values are only an array
162
+ final[final_filter] = "#{filter_value.join(';')}|or"
163
+ else
164
+ final[final_filter] = filter_value
165
+ end
166
+ end
167
+ final
168
+ end
169
+
170
+ def filters
171
+ @filters ||= {}
172
+ end
173
+
174
+ def filters=(new_filters)
175
+ raise ArgumentError.new('filters= -> new_filters MUST be a hash, even empty') unless new_filters.is_a?(Hash)
176
+ @filters = new_filters
177
+ end
178
+
179
+ def add_filters()
180
+ self.filters
181
+ end
182
+
183
+ end
184
+
185
+ def to_s
186
+ "#<#{self.class.to_s} external_code=#{@external_code.inspect} name=#{@name.inspect} >"
187
+ end
188
+
189
+ class Error
190
+ attr_accessor :code, :message, :payload, :request
191
+ end
192
+
193
+ end
194
+
195
+ class Transilien::FakeMicroService < Transilien::MicroService
196
+
197
+ class << self
198
+ def http(dummy=nil)
199
+ raise "FakeMicroService: you can't request a connection a connection. Use #from_node instead"
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,5 @@
1
+ class Transilien::Mode < Transilien::MicroService
2
+ def mode_type
3
+ @mode_type ||= Transilien::ModeType.find(mode_type_external_code: payload['ModeTypeExternalCode']).try(:first)
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ class Transilien::ModeType < Transilien::MicroService
2
+ end
@@ -0,0 +1,2 @@
1
+ class Transilien::Network < Transilien::MicroService
2
+ end
@@ -0,0 +1,13 @@
1
+ class Transilien::Route < Transilien::MicroService
2
+ def line
3
+ @line ||= Transilien::Line.from_node(payload.at('Line'), access_time)
4
+ end
5
+ end
6
+
7
+ class Transilien::City < Transilien::FakeMicroService
8
+ attr_accessor :code
9
+ def from_node(node, access_time)
10
+ item = super(node, access_time)
11
+ item.code(node['CityCode'])
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ class Transilien::Stop < Transilien::FakeMicroService
2
+ attr_accessor :stop_time, :arrival_time, :name_at_stop
3
+
4
+ class << self
5
+ def from_node(node, access_time)
6
+ item = new
7
+ item.payload = node
8
+
9
+ stop_node = node.at('StopTime')
10
+ item.stop_time = !stop_node.children.empty? && ::Transilien::Time.from_node(stop_node, access_time)
11
+
12
+ arrival_node = node.at('StopArrivalTime')
13
+ item.arrival_time = !arrival_node.children.empty? && ::Transilien::Time.from_node(arrival_node, access_time)
14
+
15
+ item.name_at_stop = node.at('VehicleJourneyNameAtStop') && node.at('VehicleJourneyNameAtStop').text
16
+ item
17
+ end
18
+ end
19
+
20
+ def stop_point
21
+ @stop_point ||= Transilien::StopPoint.from_node(payload.at('StopPoint'), access_time)
22
+ end
23
+
24
+ def name # objective: enable caching
25
+ "#{stop_time.name}||#{arrival_time.name}"
26
+ end
27
+ end
@@ -0,0 +1,52 @@
1
+ class Transilien::StopArea < Transilien::MicroService
2
+ def main_stop_area
3
+ @main_stop_area ||= payload['MainStopArea'] == '1'
4
+ end
5
+
6
+ def multi_modal
7
+ @multi_modal ||= payload['MultiModal'] == '1'
8
+ end
9
+
10
+ def car_park
11
+ @car_park ||= payload['CarPark'] == '1'
12
+ end
13
+
14
+ def main_connection
15
+ @main_connection ||= payload['MainConnection'] == '1'
16
+ end
17
+
18
+ def additional_data
19
+ @additional_data ||= payload['AdditionalData']
20
+ end
21
+
22
+ def resa_rail_code
23
+ @resa_rail_code ||= payload['ResaRailCode']
24
+ end
25
+
26
+ def coord
27
+ @coord ||= {
28
+ x: payload.at('Coord').at('CoordX').content.sub(',','.').to_f,
29
+ y: payload.at('Coord').at('CoordY').content.sub(',','.').to_f
30
+ }
31
+ end
32
+
33
+ def modes
34
+ @modes ||= begin
35
+ modes = []
36
+ payload.at('ModeList').children.each do |mode|
37
+ modes << Transilien::ModeType.from_node(mode, access_time)
38
+ end
39
+ modes
40
+ end
41
+ end
42
+ alias_method :mode_types, :modes # ModeList ≠ ModeTypeList : bad naming from SNCF
43
+
44
+ def city
45
+ @city ||= Transilien::City.from_node(payload.at('City'), access_time)
46
+ end
47
+
48
+ def hangs
49
+ @hangs ||= payload.at('HangList').children
50
+ end
51
+
52
+ end
@@ -0,0 +1,41 @@
1
+ class Transilien::StopPoint < Transilien::MicroService
2
+ def fare_zone
3
+ @fare_zone ||= payload['FareZone']
4
+ end
5
+
6
+ def address
7
+ payload.at('StopPointAddress')
8
+ end
9
+ alias_method :stop_point_address, :address
10
+
11
+ def equipment
12
+ @equipment ||= {
13
+ sheltered: (payload.at('Equipment')['Sheltered'] == 'True'),
14
+ mimp_access: (payload.at('Equipment')['MIPAccess'] == 'True'),
15
+ elevator: (payload.at('Equipment')['Elevator'] == 'True'),
16
+ escalator: (payload.at('Equipment')['Escalator'] == 'True'),
17
+ bike_accepted: (payload.at('Equipment')['BikeAccepted'] == 'True'),
18
+ bike_depot: (payload.at('Equipment')['BikeDepot'] == 'True')
19
+ }
20
+ end
21
+
22
+ def mode
23
+ @mode ||= Transilien::Mode.from_node(payload.at('Mode'), access_time)
24
+ end
25
+
26
+ def city
27
+ @city ||= Transilien::City.from_node(payload.at('City'), access_time)
28
+ end
29
+
30
+ def stop_area
31
+ #@stop_area ||= Transilien::StopArea.from_node(payload.at('StopArea'), access_time)
32
+ @stop_area ||= Transilien::StopArea.find(stop_area_external_code: payload.at('StopArea')['StopAreaExternalCode']).first
33
+ end
34
+
35
+ def coord
36
+ @coord ||= {
37
+ x: payload.at('Coord').at('CoordX').content.sub(',','.').to_f,
38
+ y: payload.at('Coord').at('CoordY').content.sub(',','.').to_f
39
+ }
40
+ end
41
+ end
@@ -0,0 +1,20 @@
1
+ class Transilien::Time < Transilien::FakeMicroService
2
+ attr_accessor :total_seconds, :day, :hour, :minute
3
+
4
+ class << self
5
+ def from_node(node, access_time)
6
+ item = new
7
+ item.payload = node
8
+
9
+ item.total_seconds = node.at('TotalSeconds').text
10
+ item.day = node.at('Day').text
11
+ item.hour = node.at('Hour').text
12
+ item.minute = node.at('Minute').text
13
+ item
14
+ end
15
+ end
16
+
17
+ def name # objective: enable caching
18
+ "#{day}:#{hour}:#{minutes}|#{total_seconds}"
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ class Transilien::VehicleJourney < Transilien::MicroService
2
+ def route
3
+ @route ||= Transilien::Route.from_node(payload.at('Route'), access_time)
4
+ end
5
+ def mode
6
+ @mode ||= Transilien::Mode.from_node(payload.at('Mode'), access_time)
7
+ end
8
+ def company
9
+ @company ||= Transilien::Company.from_node(payload.at('Company'), access_time)
10
+ end
11
+ def vehicle
12
+ @vehicle ||= payload.at('Vehicle')
13
+ end
14
+ def validity_pattern
15
+ @validity_pattern ||= payload.at('ValidityPattern')
16
+ end
17
+
18
+ def stops
19
+ @stops ||= begin
20
+ stops_nodes = payload.at('StopList')
21
+ stops_nodes.children.map do |stop_node|
22
+ Transilien::Stop.from_node(stop_node, access_time)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module Transilien
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,22 @@
1
+ require "./lib/transilien/version.rb"
2
+ require "./lib/transilien/micro_service.rb"
3
+ require "./lib/transilien/time.rb"
4
+ require "./lib/transilien/network.rb"
5
+ require "./lib/transilien/mode_type.rb"
6
+ require "./lib/transilien/line.rb"
7
+ require "./lib/transilien/route.rb"
8
+ require "./lib/transilien/stop_point.rb"
9
+ require "./lib/transilien/stop_area.rb"
10
+ require "./lib/transilien/vehicle_journey.rb"
11
+ require "./lib/transilien/mode.rb"
12
+ require "./lib/transilien/company.rb"
13
+ require "./lib/transilien/stop.rb"
14
+
15
+ module Transilien
16
+ def self.date(time)
17
+ time.strftime('%Y|%m|%d')
18
+ end
19
+ def self.time(time)
20
+ time.strftime('%H|%M')
21
+ end
22
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ describe Transilien::MicroService do
3
+ it 'should generate empty params for nothing' do
4
+ Transilien::MicroService.filters = {}
5
+ Transilien::MicroService.params.empty?.should eq(true)
6
+ end
7
+
8
+ it 'should generate correct params for one simple filter' do
9
+ Transilien::MicroService.filters = {:network_external_code => 'DU804'}
10
+ Transilien::MicroService.params.should eq({'NetworkExternalCode' => 'DU804'})
11
+ end
12
+
13
+ it 'should generate correct params for two simple filters' do
14
+ Transilien::MicroService.filters = {:network_external_code => 'DU804', :route_external_code => 'DUA8008030781013'}
15
+ Transilien::MicroService.params.should eq({'NetworkExternalCode' => 'DU804', 'RouteExternalCode' => 'DUA8008030781013'})
16
+ end
17
+
18
+ it 'should generate correct params for one simple filter with Array, or is the default logic operator for arrays' do
19
+ Transilien::MicroService.filters = {:network_external_code => ['DU804', 'DU805']}
20
+ Transilien::MicroService.params.should eq({'NetworkExternalCode' => 'DU804;DU805|or'})
21
+ end
22
+
23
+ it 'should generate correct params for two simple filters with Array, or is the default logic operator for arrays' do
24
+ Transilien::MicroService.filters = {:network_external_code => ['DU804', 'DU805'], :route_external_code => ['DUA8008030781013', 'DUA8008031050001']}
25
+ Transilien::MicroService.params.should eq({'NetworkExternalCode' => 'DU804;DU805|or', 'RouteExternalCode' => 'DUA8008030781013;DUA8008031050001|or'})
26
+ end
27
+
28
+ it 'should generate correct params for one complex filter' do
29
+ Transilien::MicroService.filters = {:network_external_code => { :and => ['DU804', 'DU805']}}
30
+ Transilien::MicroService.params.should eq({'NetworkExternalCode' => 'DU804;DU805|and'})
31
+ end
32
+
33
+ it 'should generate correct params for two complex filters' do
34
+ Transilien::MicroService.filters = {:network_external_code => { :and => ['DU804', 'DU805']}, :route_external_code => { :or => ['DUA8008030781013', 'DUA8008031050001']}}
35
+ Transilien::MicroService.params.should eq({'NetworkExternalCode' => 'DU804;DU805|and', 'RouteExternalCode' => 'DUA8008030781013;DUA8008031050001|or'})
36
+ end
37
+
38
+ it 'shoud generate correct params for a complex filter and a simple one' do
39
+ Transilien::MicroService.filters = {:stop_area_external_code => { :and => ['DUA8738400', 'DUA8738179']}, date: '2013|12|19', start_time: '17|00', end_time: '22|30'}
40
+ Transilien::MicroService.params.should eq({'StopAreaExternalCode' => 'DUA8738400;DUA8738179|and', 'Date' => '2013|12|19', 'StartTime' => '17|00', 'EndTime' => '22|30'})
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'transilien_microservices' # and any other gems you need
5
+
6
+ RSpec.configure do |config|
7
+ # some (optional) config here
8
+ end
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="ISO-8859-1"?><ActionVehicleJourneyList><Params Function="api"><SafeMode>0</SafeMode><action>PTReferential</action><RequestedType>vehiclejourneylist</RequestedType><routeexternalcode>DUA8008540420003;DUA8008540430008;DUA8008540430010;DUA8008540430005;DUA8008544000030;DUA8008540440001|or</routeexternalcode><date>2013|10|24</date><starttime>18|19</starttime><endtime>18|21</endtime><networkexternalcode>dua801;dua802;dua803;dua804;dua805;dua850;dua851;dua852;dua853;dua854;dua855</networkexternalcode><interface>1_10</interface><login>opendata</login></Params><VehicleJourneyList VehicleJourneyCount="1"><VehicleJourney VehicleJourneyIdx="391578" VehicleJourneyId="391577" VehicleJourneyName="GETO" VehicleJourneyRouteIdx="9991" VehicleJourneyExternalCode="DUASN137131F01002" IsAdapted="0"><Route RouteIdx="9991" RouteId="9990" RouteName="Gare St Lazare =&gt; Gisors Via Montgeroult" RouteExternalCode="DUA8008540430010" IsForward="1" RouteLineIdx="1660" IsFrequence="0" IsAdapted="0"><Line LineIdx="1660" LineId="1659" LineName="Gare St Lazare =&gt; Gisors Via Montgeroult" LineCode="J" LineExternalCode="DUA800854043" LineAdditionalData="" SortOrder="1660" LineColor="000000" HasAdaptedRoute="0"><ModeType ModeTypeIdx="15" ModeTypeExternalCode="RapidTransit" ModeTypeName="Train de banlieue / RER"/><Network NetworkIdx="76" NetworkId="8" NetworkName="Paris St Lazare" NetworkExternalCode="DUA854"></Network><Forward ForwardName="A"></Forward><Backward BackwardName=""></Backward></Line></Route><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><Company CompanyIdx="50" CompanyId="49" CompanyName="SNCF" CompanyExternalCode="DUA800"/><Vehicle VehicleIdx="0" VehicleId="-1" VehicleName="" VehicleExternalCode="" MIPAccess="False" BikeAccepted="False" AirConditioned="False"/><ValidityPattern ValidityPatternBeginningDay="2013|09|27" IsAdapted="0" ValidityPatternIdx="9987">000000000000000000000100111110011110001111100011110011111001111100111110011111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000</ValidityPattern><StopList StopCount="15"><Stop StopIdx="6981526" StopPointIdx="1889" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="0" VehicleIdx="0"><StopTime><TotalSeconds>66000</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>20</Minute></StopTime><StopArrivalTime/><StopPoint StopPointIdx="1889" StopPointId="39699" StopPointName="PARIS SAINT-LAZARE" StopPointExternalCode="DUA8738400" FareZone="1"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1" CityId="0" CityName="Paris" CityExternalCode="75056" CityCode="75004"></City><StopArea StopAreaIdx="592" StopAreaId="317" StopAreaName="PARIS SAINT-LAZARE" StopAreaExternalCode="DUA8738400" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="1" AdditionalData="" ResaRailCode=""><Coord><CoordX>599136,94</CoordX><CoordY>2430773,22</CoordY></Coord></StopArea><Coord><CoordX>599140,00</CoordX><CoordY>2430792,00</CoordY></Coord></StopPoint><VehicleJourneyNameAtStop>37131</VehicleJourneyNameAtStop></Stop><Stop StopIdx="7162918" StopPointIdx="39148" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="1" VehicleIdx="0"><StopTime><TotalSeconds>66960</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>36</Minute></StopTime><StopArrivalTime><TotalSeconds>66900</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>35</Minute></StopArrivalTime><StopPoint StopPointIdx="39148" StopPointId="39671" StopPointName="HERBLAY" StopPointExternalCode="DUA8738188" FareZone="4"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="3" ModeId="3" ModeName="BUS" ModeExternalCode="RATP" ModeTypeExternalCode="Bus"/><City CityIdx="1185" CityId="1184" CityName="Herblay" CityExternalCode="95306" CityCode="95220"></City><StopArea StopAreaIdx="16806" StopAreaId="17633" StopAreaName="HERBLAY" StopAreaExternalCode="DUA8738188" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>587165,67</CoordX><CoordY>2443663,10</CoordY></Coord></StopArea><Coord><CoordX>587212,66</CoordX><CoordY>2443585,69</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7219144" StopPointIdx="13263" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="2" VehicleIdx="0"><StopTime><TotalSeconds>67260</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>41</Minute></StopTime><StopArrivalTime><TotalSeconds>67200</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>40</Minute></StopArrivalTime><StopPoint StopPointIdx="13263" StopPointId="39672" StopPointName="CONFLANS SAINTE-HONORINE" StopPointExternalCode="DUA8738189" FareZone="5"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="3" ModeId="3" ModeName="BUS" ModeExternalCode="RATP" ModeTypeExternalCode="Bus"/><City CityIdx="579" CityId="578" CityName="Conflans-Sainte-Honorine" CityExternalCode="78172" CityCode="78700"></City><StopArea StopAreaIdx="5848" StopAreaId="6101" StopAreaName="CONFLANS SAINTE-HONORINE" StopAreaExternalCode="DUA8738189" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>582543,85</CoordX><CoordY>2444390,69</CoordY></Coord></StopArea><Coord><CoordX>582518,32</CoordX><CoordY>2444303,58</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7307988" StopPointIdx="40167" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="3" VehicleIdx="0"><StopTime><TotalSeconds>67740</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>49</Minute></StopTime><StopArrivalTime><TotalSeconds>67680</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>48</Minute></StopArrivalTime><StopPoint StopPointIdx="40167" StopPointId="39565" StopPointName="PONTOISE" StopPointExternalCode="DUA8727613" FareZone="5"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="3" ModeId="3" ModeName="BUS" ModeExternalCode="RATP" ModeTypeExternalCode="Bus"/><City CityIdx="1236" CityId="1235" CityName="Pontoise" CityExternalCode="95500" CityCode="95300"></City><StopArea StopAreaIdx="17260" StopAreaId="18082" StopAreaName="PONTOISE" StopAreaExternalCode="DUA8727613" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="1" AdditionalData="" ResaRailCode=""><Coord><CoordX>582304,73</CoordX><CoordY>2449874,50</CoordY></Coord></StopArea><Coord><CoordX>582346,60</CoordX><CoordY>2449869,95</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7352269" StopPointIdx="39920" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="4" VehicleIdx="0"><StopTime><TotalSeconds>67980</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>53</Minute></StopTime><StopArrivalTime><TotalSeconds>67980</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>53</Minute></StopArrivalTime><StopPoint StopPointIdx="39920" StopPointId="39632" StopPointName="OSNY" StopPointExternalCode="DUA8738114" FareZone="5"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1227" CityId="1226" CityName="Osny" CityExternalCode="95476" CityCode="95520"></City><StopArea StopAreaIdx="17177" StopAreaId="18009" StopAreaName="OSNY" StopAreaExternalCode="DUA8738114" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>579578,22</CoordX><CoordY>2451707,92</CoordY></Coord></StopArea><Coord><CoordX>579625,09</CoordX><CoordY>2451695,58</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7395775" StopPointIdx="37513" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="5" VehicleIdx="0"><StopTime><TotalSeconds>68220</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>57</Minute></StopTime><StopArrivalTime><TotalSeconds>68220</TotalSeconds><Day>0</Day><Hour>18</Hour><Minute>57</Minute></StopArrivalTime><StopPoint StopPointIdx="37513" StopPointId="39633" StopPointName="BOISSY L'AILLERIE" StopPointExternalCode="DUA8738115" FareZone="5"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1123" CityId="1122" CityName="Boissy-l'Aillerie" CityExternalCode="95078" CityCode="95650"></City><StopArea StopAreaIdx="16097" StopAreaId="16902" StopAreaName="BOISSY L'AILLERIE" StopAreaExternalCode="DUA8738115" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>577234,07</CoordX><CoordY>2453083,08</CoordY></Coord></StopArea><Coord><CoordX>577221,21</CoordX><CoordY>2453137,24</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7428308" StopPointIdx="39612" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="6" VehicleIdx="0"><StopTime><TotalSeconds>68400</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>00</Minute></StopTime><StopArrivalTime><TotalSeconds>68400</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>00</Minute></StopArrivalTime><StopPoint StopPointIdx="39612" StopPointId="39634" StopPointName="MONTGEROULT COURCELLES" StopPointExternalCode="DUA8738116" FareZone="5"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1210" CityId="1209" CityName="Montgeroult" CityExternalCode="95422" CityCode="95650"></City><StopArea StopAreaIdx="17040" StopAreaId="17878" StopAreaName="MONTGEROULT COURCELLES" StopAreaExternalCode="DUA8738116" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>575713,63</CoordX><CoordY>2453675,60</CoordY></Coord></StopArea><Coord><CoordX>575670,74</CoordX><CoordY>2453661,18</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7471886" StopPointIdx="41239" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="7" VehicleIdx="0"><StopTime><TotalSeconds>68640</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>04</Minute></StopTime><StopArrivalTime><TotalSeconds>68640</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>04</Minute></StopArrivalTime><StopPoint StopPointIdx="41239" StopPointId="39635" StopPointName="US" StopPointExternalCode="DUA8738117" FareZone="5"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1265" CityId="1264" CityName="Us" CityExternalCode="95625" CityCode="95450"></City><StopArea StopAreaIdx="17670" StopAreaId="18535" StopAreaName="US" StopAreaExternalCode="DUA8738117" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>573255,15</CoordX><CoordY>2455803,84</CoordY></Coord></StopArea><Coord><CoordX>573255,15</CoordX><CoordY>2455803,84</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7514946" StopPointIdx="40851" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="8" VehicleIdx="0"><StopTime><TotalSeconds>68880</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>08</Minute></StopTime><StopArrivalTime><TotalSeconds>68880</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>08</Minute></StopArrivalTime><StopPoint StopPointIdx="40851" StopPointId="39636" StopPointName="SANTEUIL LE PERCHAY" StopPointExternalCode="DUA8738118" FareZone="5"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1255" CityId="1254" CityName="Santeuil" CityExternalCode="95584" CityCode="95640"></City><StopArea StopAreaIdx="17515" StopAreaId="18374" StopAreaName="SANTEUIL LE PERCHAY" StopAreaExternalCode="DUA8738118" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>571574,93</CoordX><CoordY>2458528,71</CoordY></Coord></StopArea><Coord><CoordX>571718,79</CoordX><CoordY>2458460,14</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7567792" StopPointIdx="37934" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="9" VehicleIdx="0"><StopTime><TotalSeconds>69180</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>13</Minute></StopTime><StopArrivalTime><TotalSeconds>69120</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>12</Minute></StopArrivalTime><StopPoint StopPointIdx="37934" StopPointId="39637" StopPointName="CHARS" StopPointExternalCode="DUA8738119" FareZone="5"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1137" CityId="1136" CityName="Chars" CityExternalCode="95142" CityCode="95750"></City><StopArea StopAreaIdx="16228" StopAreaId="17039" StopAreaName="CHARS" StopAreaExternalCode="DUA8738119" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>570834,99</CoordX><CoordY>2462829,40</CoordY></Coord></StopArea><Coord><CoordX>570834,99</CoordX><CoordY>2462829,40</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7620012" StopPointIdx="41587" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="10" VehicleIdx="0"><StopTime><TotalSeconds>69480</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>18</Minute></StopTime><StopArrivalTime><TotalSeconds>69420</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>17</Minute></StopArrivalTime><StopPoint StopPointIdx="41587" StopPointId="39638" StopPointName="LA VILLETERTRE" StopPointExternalCode="DUA8738120" FareZone="100"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1321" CityId="1295" CityName="Lavilletertre" CityExternalCode="60356" CityCode="60240"></City><StopArea StopAreaIdx="17825" StopAreaId="18698" StopAreaName="LA VILLETERTRE" StopAreaExternalCode="DUA8738120" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>569663,00</CoordX><CoordY>2467290,00</CoordY></Coord></StopArea><Coord><CoordX>569663,00</CoordX><CoordY>2467290,00</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7661292" StopPointIdx="41590" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="11" VehicleIdx="0"><StopTime><TotalSeconds>69720</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>22</Minute></StopTime><StopArrivalTime><TotalSeconds>69660</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>21</Minute></StopArrivalTime><StopPoint StopPointIdx="41590" StopPointId="39639" StopPointName="LIANCOURT SAINT-PIERRE" StopPointExternalCode="DUA8738121" FareZone="100"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1323" CityId="1296" CityName="Liancourt-Saint-Pierre" CityExternalCode="60361" CityCode="60240"></City><StopArea StopAreaIdx="17827" StopAreaId="18700" StopAreaName="LIANCOURT SAINT-PIERRE" StopAreaExternalCode="DUA8738121" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>568562,00</CoordX><CoordY>2469312,00</CoordY></Coord></StopArea><Coord><CoordX>568562,00</CoordX><CoordY>2469312,00</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7721453" StopPointIdx="41555" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="12" VehicleIdx="0"><StopTime><TotalSeconds>70080</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>28</Minute></StopTime><StopArrivalTime><TotalSeconds>70080</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>28</Minute></StopArrivalTime><StopPoint StopPointIdx="41555" StopPointId="39640" StopPointName="CHAUMONT EN VEXIN" StopPointExternalCode="DUA8738122" FareZone="100"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1307" CityId="1290" CityName="Chaumont-en-Vexin" CityExternalCode="60143" CityCode="60240"></City><StopArea StopAreaIdx="17804" StopAreaId="18677" StopAreaName="CHAUMONT EN VEXIN" StopAreaExternalCode="DUA8738122" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>566204,00</CoordX><CoordY>2473835,00</CoordY></Coord></StopArea><Coord><CoordX>566204,00</CoordX><CoordY>2473835,00</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7771163" StopPointIdx="41679" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="13" VehicleIdx="0"><StopTime><TotalSeconds>70380</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>33</Minute></StopTime><StopArrivalTime><TotalSeconds>70380</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>33</Minute></StopArrivalTime><StopPoint StopPointIdx="41679" StopPointId="39641" StopPointName="TRIE CHATEAU" StopPointExternalCode="DUA8738123" FareZone="100"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1346" CityId="1305" CityName="Trie-Ch�teau" CityExternalCode="60644" CityCode="60590"></City><StopArea StopAreaIdx="17884" StopAreaId="18760" StopAreaName="TRIE CHATEAU" StopAreaExternalCode="DUA8738123" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>562249,00</CoordX><CoordY>2476227,00</CoordY></Coord></StopArea><Coord><CoordX>562249,00</CoordX><CoordY>2476227,00</CoordY></Coord></StopPoint></Stop><Stop StopIdx="7800949" StopPointIdx="41725" VehicleJourneyIdx="391578" HourNumber="0" MinuteNumber="0" DestinationPos="-1" ODTIdx="0" ODTPos="-1" ValidityPatternSetCommentPos="0" StopOrder="14" VehicleIdx="0"><StopTime><TotalSeconds>70560</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>36</Minute></StopTime><StopArrivalTime><TotalSeconds>70560</TotalSeconds><Day>0</Day><Hour>19</Hour><Minute>36</Minute></StopArrivalTime><StopPoint StopPointIdx="41725" StopPointId="39642" StopPointName="GISORS" StopPointExternalCode="DUA8738124" FareZone="100"><StopPointAddress StopPointAddressName="" StopPointAddressNumber="" StopPointAddressTypeName=""/><Equipment Sheltered="False" MIPAccess="False" Elevator="False" Escalator="False" BikeAccepted="False" BikeDepot="False"/><Mode ModeIdx="12" ModeId="12" ModeName="Train" ModeExternalCode="Train" ModeTypeExternalCode="RapidTransit"/><City CityIdx="1360" CityId="1309" CityName="Gisors" CityExternalCode="27284" CityCode="27140"></City><StopArea StopAreaIdx="17907" StopAreaId="18785" StopAreaName="GISORS" StopAreaExternalCode="DUA8738124" MainStopArea="1" MultiModal="0" CarPark="0" MainConnection="0" AdditionalData="" ResaRailCode=""><Coord><CoordX>559847,00</CoordX><CoordY>2476541,00</CoordY></Coord></StopArea><Coord><CoordX>559847,00</CoordX><CoordY>2476541,00</CoordY></Coord></StopPoint></Stop></StopList></VehicleJourney></VehicleJourneyList><PagerInfo ResponseCount="1" ResponseStartIndex="0" TotalCount="1"/></ActionVehicleJourneyList>
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ raw_payload = %(
4
+
5
+ )
6
+
7
+ describe Transilien::MicroService do
8
+ it 'should get a set of Stop' do
9
+ # This payload was generated from
10
+ # http://ms.api.transilien.com/?action=VehicleJourneyList&RouteExternalCode=DUA8008540420003%3BDUA8008540430008%3BDUA8008540430010%3BDUA8008540430005%3BDUA8008544000030%3BDUA8008540440001|or&Date=2013|10|24&StartTime=18|19&EndTime=18|21
11
+ vj = Transilien::VehicleJourney.from_node(Nokogiri.XML((Pathname.new('spec') + 'vehicle_journey_alone.xml').read), Time.now)
12
+ vj.stops.first.class.should eql(Transilien::Stop)
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'transilien/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "transilien_microservices"
8
+ gem.version = Transilien::VERSION
9
+ gem.authors = ["Thomas Lecavelier"]
10
+ gem.email = ["thomas-gems@lecavelier.name"]
11
+ gem.description = %q{Implements SNCF Transilien micro-services API: enable access to their theoric offer.}
12
+ gem.summary = %q{See http://test.data-sncf.com/index.php?p=transilien}
13
+ gem.homepage = "https://github.com/ook/transilien_microservices"
14
+ gem.license = 'MIT'
15
+
16
+ gem.add_runtime_dependency('faraday', '>= 0.8.4') # HTTP(S) connections
17
+ gem.add_runtime_dependency('nokogiri', '>= 1.5.5') # XML parsing
18
+
19
+ gem.files = `git ls-files`.split($/)
20
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
22
+ gem.require_paths = ["lib"]
23
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: transilien_microservices
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Thomas Lecavelier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.8.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.8.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: nokogiri
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.5.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.5.5
41
+ description: 'Implements SNCF Transilien micro-services API: enable access to their
42
+ theoric offer.'
43
+ email:
44
+ - thomas-gems@lecavelier.name
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - .rspec
51
+ - .ruby-version
52
+ - Gemfile
53
+ - Gemfile.lock
54
+ - Guardfile
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - lib/transilien/company.rb
59
+ - lib/transilien/line.rb
60
+ - lib/transilien/micro_service.rb
61
+ - lib/transilien/mode.rb
62
+ - lib/transilien/mode_type.rb
63
+ - lib/transilien/network.rb
64
+ - lib/transilien/route.rb
65
+ - lib/transilien/stop.rb
66
+ - lib/transilien/stop_area.rb
67
+ - lib/transilien/stop_point.rb
68
+ - lib/transilien/time.rb
69
+ - lib/transilien/vehicle_journey.rb
70
+ - lib/transilien/version.rb
71
+ - lib/transilien_microservices.rb
72
+ - spec/microservice_spec.rb
73
+ - spec/spec_helper.rb
74
+ - spec/vehicle_journey_alone.xml
75
+ - spec/vehicle_journey_spec.rb
76
+ - transilien_microservices.gemspec
77
+ homepage: https://github.com/ook/transilien_microservices
78
+ licenses:
79
+ - MIT
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.0.0
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: See http://test.data-sncf.com/index.php?p=transilien
101
+ test_files:
102
+ - spec/microservice_spec.rb
103
+ - spec/spec_helper.rb
104
+ - spec/vehicle_journey_alone.xml
105
+ - spec/vehicle_journey_spec.rb
106
+ has_rdoc: