routing 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +22 -0
- data/Gemfile +8 -0
- data/Guardfile +5 -0
- data/LICENSE +22 -0
- data/README.md +54 -0
- data/Rakefile +10 -0
- data/lib/routing.rb +77 -0
- data/lib/routing/adapter.rb +22 -0
- data/lib/routing/adapter/navteq.rb +83 -0
- data/lib/routing/adapter/test.rb +24 -0
- data/lib/routing/geo_point.rb +34 -0
- data/lib/routing/middleware.rb +29 -0
- data/lib/routing/parser.rb +26 -0
- data/lib/routing/parser/navteq_simple.rb +112 -0
- data/lib/routing/version.rb +3 -0
- data/routing.gemspec +24 -0
- data/spec/fixtures/navteq/error_response.json +1 -0
- data/spec/fixtures/navteq/response.json +357 -0
- data/spec/routing/adapter/navteq_spec.rb +59 -0
- data/spec/routing/adapter/test_spec.rb +29 -0
- data/spec/routing/geo_point_spec.rb +40 -0
- data/spec/routing/parser/navteq_simple_spec.rb +81 -0
- data/spec/routing_spec.rb +82 -0
- data/spec/spec_helper.rb +23 -0
- metadata +113 -0
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.navteq_host
|
6
|
+
.yardoc
|
7
|
+
.rspec
|
8
|
+
.rvmrc
|
9
|
+
Gemfile.lock
|
10
|
+
InstalledFiles
|
11
|
+
_yardoc
|
12
|
+
coverage
|
13
|
+
doc/
|
14
|
+
lib/bundler/man
|
15
|
+
pkg
|
16
|
+
rdoc
|
17
|
+
spec/reports
|
18
|
+
test/tmp
|
19
|
+
test/version_tmp
|
20
|
+
tmp
|
21
|
+
**/.DS_Store
|
22
|
+
.DS_Store
|
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 flinc AG
|
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,54 @@
|
|
1
|
+
# Routing
|
2
|
+
|
3
|
+
Provides a generic interface for routing services that can by used to calculate directions between geolocations.
|
4
|
+
|
5
|
+
It aims to make parsing and use-case specific data handling easy trough an extendable middleware stack (think of rack middleware for your routing service).
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
start = Routing::GeoPoint.new(:lat => 49, :lng => 9)
|
11
|
+
destination = Routing::GeoPoint.new(:lat => 48, :lng => 8.9)
|
12
|
+
|
13
|
+
route = Routing.new.calculate(start, destination)
|
14
|
+
```
|
15
|
+
|
16
|
+
### Middleware
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
routing = Routing.new do |r|
|
20
|
+
r.use MyMiddleware.new
|
21
|
+
r.use MyRoutingCache.new
|
22
|
+
end
|
23
|
+
|
24
|
+
route = routing.calculate(start, destination)
|
25
|
+
```
|
26
|
+
|
27
|
+
### Custom Adapters
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
routing = Routing.new(MyAdapter.new)
|
31
|
+
route = routing.calculate(start, destination)
|
32
|
+
```
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
Add this line to your application's Gemfile:
|
37
|
+
|
38
|
+
gem 'routing'
|
39
|
+
|
40
|
+
And then execute:
|
41
|
+
|
42
|
+
$ bundle
|
43
|
+
|
44
|
+
Or install it yourself as:
|
45
|
+
|
46
|
+
$ gem install routing
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
1. Fork it
|
51
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
52
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
53
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
54
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/routing.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require "routing/version"
|
2
|
+
|
3
|
+
# The {Routing} class is the main entry point for the library.
|
4
|
+
class Routing
|
5
|
+
|
6
|
+
autoload :Adapter, "routing/adapter"
|
7
|
+
autoload :GeoPoint, "routing/geo_point"
|
8
|
+
autoload :Middleware, "routing/middleware"
|
9
|
+
autoload :Parser, "routing/parser"
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
# Sets the default adapter/routing service.
|
14
|
+
#
|
15
|
+
# @return [Object] Default adapter.
|
16
|
+
attr_writer :default_adapter
|
17
|
+
|
18
|
+
# The default adapter/routing service that is used, if no one is specified.
|
19
|
+
# Currently this is {Routing::Adapter::Navteq}.
|
20
|
+
#
|
21
|
+
# @return [Object] Current default adapter.
|
22
|
+
def default_adapter
|
23
|
+
@default_adapter ||= Routing::Adapter::Navteq.new
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_accessor :middlewares
|
29
|
+
|
30
|
+
# Creates a new instance of the routing class
|
31
|
+
#
|
32
|
+
# @param [Object] adapter Adapter for the routing service that should be used, defaults to {Routing::Adapter::Navteq}.
|
33
|
+
def initialize(adapter = self.class.default_adapter)
|
34
|
+
@adapter = adapter
|
35
|
+
@middlewares = []
|
36
|
+
|
37
|
+
yield(self) if block_given?
|
38
|
+
end
|
39
|
+
|
40
|
+
# Calculates a route for the passed {GeoPoint}s.
|
41
|
+
# These will be passed through the middleware stack and will
|
42
|
+
# finally be given to the configured adapter which calculates a route out of it.
|
43
|
+
#
|
44
|
+
# @param [Array<GeoPoint>] geo_points
|
45
|
+
# An array of geo points to calculate a route of.
|
46
|
+
#
|
47
|
+
# @return [Array<GeoPoint>]
|
48
|
+
# An array of geo points that represent the calculated route.
|
49
|
+
def calculate(*geo_points)
|
50
|
+
calculate_with_stack(geo_points.flatten, middlewares + [@adapter])
|
51
|
+
end
|
52
|
+
|
53
|
+
# Adds an object to the middleware stack.
|
54
|
+
#
|
55
|
+
# @param [Routing::Middleware] middleware The middleware to append to the stack.
|
56
|
+
#
|
57
|
+
# @return [Array<Routing::Middleware>] Updated list of used middlewares.
|
58
|
+
def use(middleware)
|
59
|
+
@middlewares << middleware
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Helper method that will iterate through the middleware stack.
|
65
|
+
#
|
66
|
+
# @param [Array<GeoPoint>] geo_points
|
67
|
+
# The array of geo points to be passed to the middleware.
|
68
|
+
#
|
69
|
+
# @param [Routing::Middleware] stack
|
70
|
+
# The remaining stack of middlewares to iterate through.
|
71
|
+
def calculate_with_stack(geo_points, stack)
|
72
|
+
stack.shift.calculate(geo_points) do |gp|
|
73
|
+
calculate_with_stack(gp, stack)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Routing
|
2
|
+
|
3
|
+
# The {Routing::Adapter} namespace holds classes that are responsible to accept an
|
4
|
+
# array of {Routing::GeoPoint}s and calculate a route based on these positions.
|
5
|
+
#
|
6
|
+
# Most commonly they will get the calculated route from a webservice and will create a compatible
|
7
|
+
# return value by using a matching {Routing::Parser}.
|
8
|
+
#
|
9
|
+
# The end-point of the middleware stack is always the chosen adapter of a {Routing} instance which has to return the
|
10
|
+
# calculated route in form of {Routing::GeoPoint} objects again.
|
11
|
+
#
|
12
|
+
# Creating your own adapter:
|
13
|
+
# To connect your own routing service, you can easily implement your own adapter.
|
14
|
+
# The only requirements are an instance method called *calculate* that will take an Array of {Routing::GeoPoint}s
|
15
|
+
# as only parameters and will return an Array of {Routing::GeoPoint}s when the calculation is done.
|
16
|
+
module Adapter
|
17
|
+
|
18
|
+
autoload :Navteq, "routing/adapter/navteq"
|
19
|
+
autoload :Test, "routing/adapter/test"
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'faraday'
|
3
|
+
|
4
|
+
class Routing
|
5
|
+
module Adapter
|
6
|
+
|
7
|
+
# Adapter for a NAVTEQ LBSP Routing Service v6 server.
|
8
|
+
# It passes the {GeoPoint}s to the routing service and will return another
|
9
|
+
# Array of {GeoPoint}s, representing the calculated route.
|
10
|
+
class Navteq
|
11
|
+
|
12
|
+
ATTR_ACCESSIBLE = [ :host, :default_params ]
|
13
|
+
|
14
|
+
def initialize(options = {})
|
15
|
+
options.each do |attribute, value|
|
16
|
+
send("#{attribute}=", value) if ATTR_ACCESSIBLE.include?(attribute)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def calculate(geo_points)
|
21
|
+
parse get(geo_points)
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(geo_points)
|
25
|
+
params = default_params.merge geo_points_to_params(geo_points)
|
26
|
+
|
27
|
+
response = connection.get do |request|
|
28
|
+
request.url(service_path)
|
29
|
+
request.params = params
|
30
|
+
end
|
31
|
+
|
32
|
+
response.body
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_writer :host
|
36
|
+
|
37
|
+
def host
|
38
|
+
@host
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse(response)
|
42
|
+
::Routing::Parser::NavteqSimple.new(response).to_geo_points
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_writer :default_params
|
46
|
+
|
47
|
+
def default_params
|
48
|
+
@default_params || {
|
49
|
+
departure: Time.now.utc.iso8601,
|
50
|
+
mode0: "fastest;car",
|
51
|
+
language: "de_DE",
|
52
|
+
legattributes: "all,-links",
|
53
|
+
maneuverattributes: "position,travelTime,length,time"
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
# The path on the server to the routing service.
|
60
|
+
# This is determined by the server and should not be changed.
|
61
|
+
#
|
62
|
+
# @returns [String]
|
63
|
+
# Path to the routing service.
|
64
|
+
def service_path
|
65
|
+
"/routing/6.2/calculateroute.json"
|
66
|
+
end
|
67
|
+
|
68
|
+
def geo_points_to_params(geo_points)
|
69
|
+
Hash[geo_points.each_with_index.map { |point, i| [ "waypoint#{i}", "geo!#{point.lat},#{point.lng}" ] }]
|
70
|
+
end
|
71
|
+
|
72
|
+
def connection
|
73
|
+
@connection ||= Faraday.new(:url => host) do |builder|
|
74
|
+
builder.request :url_encoded
|
75
|
+
builder.response :logger
|
76
|
+
builder.adapter :net_http
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Routing
|
2
|
+
module Adapter
|
3
|
+
|
4
|
+
# Simple test adapter, that returns an array of {GeoPoint}s
|
5
|
+
# with values which are based on the ones it recieved.
|
6
|
+
class Test
|
7
|
+
|
8
|
+
def calculate(geo_points)
|
9
|
+
geo_points.collect do |point|
|
10
|
+
GeoPoint.new(
|
11
|
+
:lat => point.lat,
|
12
|
+
:lng => point.lng,
|
13
|
+
:original_lat => point.lat,
|
14
|
+
:original_lng => point.lng,
|
15
|
+
:relative_time => 100,
|
16
|
+
:distance => 100,
|
17
|
+
:waypoint => true
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Routing
|
2
|
+
|
3
|
+
# A {GeoPoint} object is a very simple representation of a geospatial point
|
4
|
+
# that is part of a route or can be used as base to create one.
|
5
|
+
#
|
6
|
+
# It holds the most basic textual and geospatial information that is necessary
|
7
|
+
# to describe a section of a route.
|
8
|
+
#
|
9
|
+
# The complete roundtrip from a {Routing} object through the {Routing::Middleware} to a
|
10
|
+
# {Routing::Adapter} and back uses arrays of {GeoPoint} objects as arguments.
|
11
|
+
#
|
12
|
+
# Depending on your very own use-case, you can either extend the {GeoPoint} class or
|
13
|
+
# use another class that mimicks the behavior of {GeoPoint}.
|
14
|
+
# In fact, this is just a container class which is no hard requirement in general.
|
15
|
+
# If you decide to roll your own {Adapter}, {Parser} and maybe {Middleware},
|
16
|
+
# you could replace the class completely
|
17
|
+
class GeoPoint < Struct.new(:lat, :lng, :original_lat, :original_lng, :relative_time, :distance, :waypoint, :type)
|
18
|
+
|
19
|
+
# Creates a new GeoPoint instance.
|
20
|
+
#
|
21
|
+
# @param [Hash] attributes
|
22
|
+
# Automatically sets values for the attributes that match the keys of the hash.
|
23
|
+
def initialize(attributes = {})
|
24
|
+
attributes.each do |attribute, value|
|
25
|
+
self[attribute] = value if members.include?(attribute)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def waypoint?
|
30
|
+
!!waypoint
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Routing
|
2
|
+
# An abstract middleware class to illustrate how you can build your own.
|
3
|
+
#
|
4
|
+
# @abstract
|
5
|
+
class Middleware
|
6
|
+
|
7
|
+
# The only method your own middleware has to implement to work.
|
8
|
+
#
|
9
|
+
# @param [Array<GeoPoint>] geo_points
|
10
|
+
# The array of {GeoPoint}s that should be routed.
|
11
|
+
#
|
12
|
+
# @yield [Array<GeoPoint>]
|
13
|
+
# Passes the geo_points on to the next middleware in the stack.
|
14
|
+
# Please note, that you _always_ have to call yield in order to make
|
15
|
+
# the middleware stack proceed.
|
16
|
+
#
|
17
|
+
# If you will return a value before yielding, you will cancel the rest of the middleware stack.
|
18
|
+
#
|
19
|
+
# @return [Array<GeoPoint>]
|
20
|
+
# The array of {GeoPoint}s after they have been routed.
|
21
|
+
def calculate(geo_points)
|
22
|
+
# manipulate geo points before routing here
|
23
|
+
yield(geo_points) # hand control to the next middleware in the stack
|
24
|
+
# manipulate geo points after routing here
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Routing
|
2
|
+
# The {Routing::Parser} namespace holds classes that are used to parse the
|
3
|
+
# response of a specific routing service.
|
4
|
+
#
|
5
|
+
# They are used by the matching adapters {Routing::Adapter} to create the array of {Routing::GeoPoint} objects
|
6
|
+
# to pass into the middleware.
|
7
|
+
module Parser
|
8
|
+
|
9
|
+
autoload :NavteqSimple, "routing/parser/navteq_simple"
|
10
|
+
|
11
|
+
# This error is thrown by the parsers if a calculated waypoint can't be matched
|
12
|
+
# to one of the initial passed geo points that were routed.
|
13
|
+
#
|
14
|
+
# The reason why this is important is that most routing services have a "snap-to-route"
|
15
|
+
# algorithm that will manipulate the coordinates of a passed geo point by moving it to
|
16
|
+
# the next routeable position.
|
17
|
+
# A parser should be able to determine which geo point of the response belongs to which geo point of the
|
18
|
+
# request. Otherwise this error will be thrown.
|
19
|
+
class NoMatchingMappedPositionFound < StandardError
|
20
|
+
end
|
21
|
+
|
22
|
+
class RoutingFailed < StandardError
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
class Routing
|
2
|
+
module Parser
|
3
|
+
|
4
|
+
# A very simple parser implementation for a NAVTEQ LBSP Routing Service v6.
|
5
|
+
# It converts the json response of the routing service to an Array of {GeoPoint}s.
|
6
|
+
class NavteqSimple
|
7
|
+
|
8
|
+
# Creates a new instance of the parser.
|
9
|
+
#
|
10
|
+
# @param [String] response
|
11
|
+
# A json response string of a NAVTEQ routing server.
|
12
|
+
def initialize(response)
|
13
|
+
response = JSON.parse(response)
|
14
|
+
check_for_error(response)
|
15
|
+
|
16
|
+
@route = response["Response"]["Route"].first
|
17
|
+
@overall_covered_distance = 0
|
18
|
+
@overall_relative_time = 0
|
19
|
+
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
# Converts the server response in an Array of {GeoPoint}s
|
24
|
+
#
|
25
|
+
# @return [Array<GeoPoint>]
|
26
|
+
# List of {GeoPoint}s that represent the calculated route.
|
27
|
+
def to_geo_points
|
28
|
+
legs = @route["Leg"]
|
29
|
+
geo_points = legs.map { |leg| parse_leg(leg) }.flatten
|
30
|
+
|
31
|
+
# At last we add the destination point
|
32
|
+
geo_points << parse_maneuver(legs.last["Maneuver"].last, waypoint: true)
|
33
|
+
geo_points
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Parses is single leg of the route including all its maneuvers.
|
39
|
+
#
|
40
|
+
# @param [Hash] leg
|
41
|
+
# The route leg to parse.
|
42
|
+
#
|
43
|
+
# @return [Array<GeoPoint>]
|
44
|
+
# List of {GeoPoint}s that represent the passed Leg.
|
45
|
+
def parse_leg(leg)
|
46
|
+
# Skip the last maneuver as it is the same as the first one
|
47
|
+
# of the next maneuver.
|
48
|
+
# For the last leg we parse the last maneuver right at the end
|
49
|
+
maneuvers = leg["Maneuver"][0...-1]
|
50
|
+
maneuvers.map do |maneuver|
|
51
|
+
parse_maneuver(maneuver, :waypoint => (maneuver == maneuvers.first))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Parses is single maneuver of a route leg.
|
56
|
+
#
|
57
|
+
# @param [Hash] maneuver
|
58
|
+
# The maneuver to parse.
|
59
|
+
#
|
60
|
+
# @param [Hash] attributes
|
61
|
+
# Additional attributes that should be set on the returned {GeoPoint}.
|
62
|
+
#
|
63
|
+
# @return [GeoPoint]
|
64
|
+
# A {GeoPoint} that represents the passed maneuver.
|
65
|
+
def parse_maneuver(maneuver, attributes = {})
|
66
|
+
geo_point = ::Routing::GeoPoint.new attributes.merge({
|
67
|
+
lat: maneuver["Position"]["Latitude"],
|
68
|
+
lng: maneuver["Position"]["Longitude"],
|
69
|
+
relative_time: @overall_relative_time,
|
70
|
+
distance: @overall_covered_distance
|
71
|
+
})
|
72
|
+
|
73
|
+
@overall_relative_time += maneuver["TravelTime"].to_i
|
74
|
+
@overall_covered_distance += maneuver["Length"].to_i
|
75
|
+
|
76
|
+
search_original_position(geo_point) if geo_point.waypoint?
|
77
|
+
|
78
|
+
geo_point
|
79
|
+
end
|
80
|
+
|
81
|
+
# Matches a parsed {GeoPoint} of the route response
|
82
|
+
# with the (unmapped) position of the
|
83
|
+
# corresponding {GeoPoint} of the request.
|
84
|
+
#
|
85
|
+
# @param [GeoPoint] geo_point
|
86
|
+
# Point of the response to find the initial position for.
|
87
|
+
#
|
88
|
+
# @return [GeoPoint]
|
89
|
+
# The passed in {GeoPoint}, enriched with the information about the original position in the request.
|
90
|
+
#
|
91
|
+
# @raise [NoMatchingMappedPositionFound] If no matching original position is found.
|
92
|
+
def search_original_position(geo_point)
|
93
|
+
matching_waypoint = @route["Waypoint"].detect do |waypoint|
|
94
|
+
waypoint["MappedPosition"]["Latitude"] == geo_point.lat &&
|
95
|
+
waypoint["MappedPosition"]["Longitude"] == geo_point.lng
|
96
|
+
end or raise NoMatchingMappedPositionFound
|
97
|
+
|
98
|
+
geo_point.original_lat = matching_waypoint["OriginalPosition"]["Latitude"]
|
99
|
+
geo_point.original_lng = matching_waypoint["OriginalPosition"]["Longitude"]
|
100
|
+
|
101
|
+
geo_point
|
102
|
+
end
|
103
|
+
|
104
|
+
def check_for_error(response)
|
105
|
+
if error = response['Error']
|
106
|
+
raise Routing::Parser::RoutingFailed.new("#{error['type']}(#{error['subtype']}) - #{error['Details']}")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|